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.
pull/764/head
Pieter Wuille 4 years ago
parent 8bd2b4e784
commit 5d62e3a68b

@ -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));
@ -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);

@ -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<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);

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

Loading…
Cancel
Save