From 94ef082925c324886dd8300f6dbec2ebfb8f0c18 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com> Date: Fri, 11 Sep 2020 14:33:00 -0700 Subject: [PATCH] partial bitcoin#19953: Keep spent outputs in PrecomputedTransactionData --- src/script/interpreter.cpp | 10 ++++++---- src/script/interpreter.h | 4 +++- src/validation.cpp | 20 +++++++++++++------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a9d29078108ebc..18ae1ea355e3ad 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1437,10 +1437,12 @@ uint256 GetOutputsHash(const T& txTo) } // namespace template -void PrecomputedTransactionData::Init(const T& txTo) +void PrecomputedTransactionData::Init(const T& txTo, std::vector&& 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); @@ -1454,14 +1456,14 @@ void PrecomputedTransactionData::Init(const T& txTo) template 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&& spent_outputs); +template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector&& spent_outputs); template uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 88779c334f7b94..929e50275dc2ca 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -15,6 +15,7 @@ class CPubKey; class CScript; class CTransaction; +class CTxOut; class uint256; /** Signature hash types/flags */ @@ -109,11 +110,12 @@ struct PrecomputedTransactionData { uint256 hashPrevouts, hashSequence, hashOutputs; bool m_ready = false; + std::vector m_spent_outputs; PrecomputedTransactionData() = default; template - void Init(const T& tx); + void Init(const T& tx, std::vector&& spent_outputs); template explicit PrecomputedTransactionData(const T& tx); diff --git a/src/validation.cpp b/src/validation.cpp index 640de59f93b884..ba0d4eafa202a0 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1453,14 +1453,20 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi } if (!txdata.m_ready) { - txdata.Init(tx); + std::vector 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 @@ -1468,7 +1474,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // 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()); @@ -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())));