Skip to content

Commit

Permalink
refactor: keep spent outputs in PrecomputedTransactionData
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sipa committed Oct 12, 2020
1 parent 8bd2b4e commit 5d62e3a
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 11 deletions.
10 changes: 6 additions & 4 deletions src/script/interpreter.cpp
Expand Up @@ -1294,10 +1294,12 @@ uint256 GetOutputsSHA256(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 = SHA256Uint256(GetPrevoutsSHA256(txTo));
Expand All @@ -1311,12 +1313,12 @@ void PrecomputedTransactionData::Init(const T& txTo)
template <class T>
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<CTxOut>&& spent_outputs);
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo, std::vector<CTxOut>&& spent_outputs);
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);

Expand Down
4 changes: 3 additions & 1 deletion src/script/interpreter.h
Expand Up @@ -15,6 +15,7 @@
class CPubKey;
class CScript;
class CTransaction;
class CTxOut;
class uint256;

/** Signature hash types/flags */
Expand Down Expand Up @@ -122,11 +123,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
19 changes: 13 additions & 6 deletions src/validation.cpp
Expand Up @@ -1539,13 +1539,20 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
}

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
Expand All @@ -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());
Expand All @@ -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())));
Expand Down

0 comments on commit 5d62e3a

Please sign in to comment.