Skip to content

Commit

Permalink
partial bitcoin#19953: Keep spent outputs in PrecomputedTransactionData
Browse files Browse the repository at this point in the history
  • Loading branch information
kwvg committed Feb 28, 2022
1 parent fe89e1a commit 94ef082
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 12 deletions.
10 changes: 6 additions & 4 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1437,10 +1437,12 @@ uint256 GetOutputsHash(const T& txTo)
} // namespace

template <class T>
void PrecomputedTransactionData::Init(const T& txTo)
void PrecomputedTransactionData::Init(const T& txTo, std::vector<CTxOut>&& spent_outputs)
{
assert(!m_ready);

m_spent_outputs = std::move(spent_outputs);

// Cache is calculated only for transactions with witness
if (txTo.HasWitness()) {
hashPrevouts = GetPrevoutHash(txTo);
Expand All @@ -1454,14 +1456,14 @@ void PrecomputedTransactionData::Init(const T& txTo)
template <class T>
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
{
Init(txTo);
Init(txTo, {});
}

// explicit instantiation
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
template void PrecomputedTransactionData::Init(const CTransaction& txTo);
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo);
template void PrecomputedTransactionData::Init(const CTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);

template <class T>
uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
Expand Down
4 changes: 3 additions & 1 deletion src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
class CPubKey;
class CScript;
class CTransaction;
class CTxOut;
class uint256;

/** Signature hash types/flags */
Expand Down Expand Up @@ -109,11 +110,12 @@ struct PrecomputedTransactionData
{
uint256 hashPrevouts, hashSequence, hashOutputs;
bool m_ready = false;
std::vector<CTxOut> m_spent_outputs;

PrecomputedTransactionData() = default;

template <class T>
void Init(const T& tx);
void Init(const T& tx, std::vector<CTxOut>&& spent_outputs);

template <class T>
explicit PrecomputedTransactionData(const T& tx);
Expand Down
20 changes: 13 additions & 7 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1453,22 +1453,28 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
}

if (!txdata.m_ready) {
txdata.Init(tx);
std::vector<CTxOut> spent_outputs;
spent_outputs.reserve(tx.vin.size());

for (const auto& txin : tx.vin) {
const COutPoint& prevout = txin.prevout;
const Coin& coin = inputs.AccessCoin(prevout);
assert(!coin.IsSpent());
spent_outputs.emplace_back(coin.out);
}
txdata.Init(tx, std::move(spent_outputs));
}
assert(txdata.m_spent_outputs.size() == tx.vin.size());

for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout);
assert(!coin.IsSpent());

// We very carefully only pass in things to CScriptCheck which
// are clearly committed to by tx' witness hash. This provides
// a sanity check that our caching is not introducing consensus
// failures through additional data in, eg, the coins being
// spent being checked as a part of CScriptCheck.

// Verify signature
CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata);
CScriptCheck check(txdata.m_spent_outputs[i], tx, i, flags, cacheSigStore, &txdata);
if (pvChecks) {
pvChecks->push_back(CScriptCheck());
check.swap(pvChecks->back());
Expand All @@ -1483,7 +1489,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// arguments; if so, don't trigger DoS protection to
// avoid splitting the network between upgraded and
// non-upgraded nodes.
CScriptCheck check2(coin.out, tx, i,
CScriptCheck check2(txdata.m_spent_outputs[i], tx, i,
(flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS) | SCRIPT_ENABLE_DIP0020_OPCODES, cacheSigStore, &txdata);
if (check2())
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
Expand Down

0 comments on commit 94ef082

Please sign in to comment.