From 5d62e3a68b6ea9bb03556ee1fbf5678f20be01a2 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 11 Sep 2020 14:33:00 -0700 Subject: [PATCH] refactor: keep spent outputs in PrecomputedTransactionData A BIP-341 signature message may commit to the scriptPubKeys and amounts of all spent outputs (including other ones than the input being signed for spends), so keep them available to signature hashing code. --- src/script/interpreter.cpp | 10 ++++++---- src/script/interpreter.h | 4 +++- src/validation.cpp | 19 +++++++++++++------ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 2fa7e5e1ab..c322ecb1a0 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1294,10 +1294,12 @@ uint256 GetOutputsSHA256(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 = SHA256Uint256(GetPrevoutsSHA256(txTo)); @@ -1311,12 +1313,12 @@ void PrecomputedTransactionData::Init(const T& txTo) template PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo) { - Init(txTo); + Init(txTo, {}); } // explicit instantiation -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 PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo); template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 0ff4c4bc95..64cefc0d6c 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 */ @@ -122,11 +123,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 e9c0607ced..2090d9477f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1539,13 +1539,20 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C } 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 @@ -1554,7 +1561,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C // 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()); @@ -1568,7 +1575,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C // splitting the network between upgraded and // non-upgraded nodes by banning CONSENSUS-failing // data providers. - CScriptCheck check2(coin.out, tx, i, + CScriptCheck check2(txdata.m_spent_outputs[i], tx, i, flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata); if (check2()) return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));