Merge #18401: Refactor: Initialize PrecomputedTransactionData in CheckInputScripts

f63dec189c [REFACTOR] Initialize PrecomputedTransactionData in CheckInputScripts (Pieter Wuille)

Pull request description:

  This is a single commit taken from the Schnorr/Taproot PR #17977.

  Add a default constructor to `PrecomputedTransactionData`, which doesn't initialize the struct's members. Instead they're initialized inside the `CheckInputScripts()` function. This allows a later commit to add the spent UTXOs to that structure. The spent UTXOs are required for the schnorr signature hash, since it commits to the scriptPubKeys. See https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#common-signature-message for details.

  By itself, this isn't really an improvement to the code, but I think it makes sense to separate out the refactor/moveonly commits from PR #17977 so that PR is only the logical changes needed for Schnorr/Taproot.

ACKs for top commit:
  jonatack:
    Re-ACK f63dec1  `git diff 851908d f63dec1` shows no change since last ACK.
  sipa:
    utACK f63dec189c
  theStack:
    re-ACK f63dec189c
  fjahr:
    Re-ACK f63dec189c
  ariard:
    Code Review ACK f63dec1

Tree-SHA512: ecf9154077824ae4c274b4341e985797f3648c0cb0c31cb25ce382163b923a3acbc7048683720be4ae3663501801129cd0f48c441a36f049cc304ebe9f30994e
pull/764/head
MarcoFalke 5 years ago
commit e16718a8b3
No known key found for this signature in database
GPG Key ID: CE2B75697E69A548

@ -1291,18 +1291,29 @@ uint256 GetOutputsHash(const T& txTo)
} // namespace } // namespace
template <class T> template <class T>
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo) void PrecomputedTransactionData::Init(const T& txTo)
{ {
assert(!m_ready);
// Cache is calculated only for transactions with witness // Cache is calculated only for transactions with witness
if (txTo.HasWitness()) { if (txTo.HasWitness()) {
hashPrevouts = GetPrevoutHash(txTo); hashPrevouts = GetPrevoutHash(txTo);
hashSequence = GetSequenceHash(txTo); hashSequence = GetSequenceHash(txTo);
hashOutputs = GetOutputsHash(txTo); hashOutputs = GetOutputsHash(txTo);
ready = true;
} }
m_ready = true;
}
template <class T>
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
{
Init(txTo);
} }
// explicit instantiation // explicit instantiation
template void PrecomputedTransactionData::Init(const CTransaction& txTo);
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo); template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
@ -1315,7 +1326,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
uint256 hashPrevouts; uint256 hashPrevouts;
uint256 hashSequence; uint256 hashSequence;
uint256 hashOutputs; uint256 hashOutputs;
const bool cacheready = cache && cache->ready; const bool cacheready = cache && cache->m_ready;
if (!(nHashType & SIGHASH_ANYONECANPAY)) { if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo); hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);

@ -121,7 +121,12 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
struct PrecomputedTransactionData struct PrecomputedTransactionData
{ {
uint256 hashPrevouts, hashSequence, hashOutputs; uint256 hashPrevouts, hashSequence, hashOutputs;
bool ready = false; bool m_ready = false;
PrecomputedTransactionData() = default;
template <class T>
void Init(const T& tx);
template <class T> template <class T>
explicit PrecomputedTransactionData(const T& tx); explicit PrecomputedTransactionData(const T& tx);

@ -1016,7 +1016,7 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
// scripts (ie, other policy checks pass). We perform the inexpensive // scripts (ie, other policy checks pass). We perform the inexpensive
// checks first and avoid hashing and signature verification unless those // checks first and avoid hashing and signature verification unless those
// checks pass, to mitigate CPU exhaustion denial-of-service attacks. // checks pass, to mitigate CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata(*ptx); PrecomputedTransactionData txdata;
if (!PolicyScriptChecks(args, workspace, txdata)) return false; if (!PolicyScriptChecks(args, workspace, txdata)) return false;
@ -1516,6 +1516,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
return true; return true;
} }
if (!txdata.m_ready) {
txdata.Init(tx);
}
for (unsigned int i = 0; i < tx.vin.size(); i++) { for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout; const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout); const Coin& coin = inputs.AccessCoin(prevout);
@ -2079,15 +2083,19 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
CBlockUndo blockundo; CBlockUndo blockundo;
// Precomputed transaction data pointers must not be invalidated
// until after `control` has run the script checks (potentially
// in multiple threads). Preallocate the vector size so a new allocation
// doesn't invalidate pointers into the vector, and keep txsdata in scope
// for as long as `control`.
CCheckQueueControl<CScriptCheck> control(fScriptChecks && g_parallel_script_checks ? &scriptcheckqueue : nullptr); CCheckQueueControl<CScriptCheck> control(fScriptChecks && g_parallel_script_checks ? &scriptcheckqueue : nullptr);
std::vector<PrecomputedTransactionData> txsdata(block.vtx.size());
std::vector<int> prevheights; std::vector<int> prevheights;
CAmount nFees = 0; CAmount nFees = 0;
int nInputs = 0; int nInputs = 0;
int64_t nSigOpsCost = 0; int64_t nSigOpsCost = 0;
blockundo.vtxundo.reserve(block.vtx.size() - 1); blockundo.vtxundo.reserve(block.vtx.size() - 1);
std::vector<PrecomputedTransactionData> txdata;
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
for (unsigned int i = 0; i < block.vtx.size(); i++) for (unsigned int i = 0; i < block.vtx.size(); i++)
{ {
const CTransaction &tx = *(block.vtx[i]); const CTransaction &tx = *(block.vtx[i]);
@ -2134,13 +2142,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops"); return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops");
} }
txdata.emplace_back(tx);
if (!tx.IsCoinBase()) if (!tx.IsCoinBase())
{ {
std::vector<CScriptCheck> vChecks; std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
TxValidationState tx_state; TxValidationState tx_state;
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txdata[i], g_parallel_script_checks ? &vChecks : nullptr)) { if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure // Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage()); tx_state.GetRejectReason(), tx_state.GetDebugMessage());

Loading…
Cancel
Save