[REFACTOR] Initialize PrecomputedTransactionData in CheckInputScripts

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.
pull/764/head
Pieter Wuille 6 years ago committed by John Newbery
parent 5504703a9f
commit f63dec189c

@ -1279,18 +1279,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);
@ -1303,7 +1314,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;
@ -1512,6 +1512,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);
@ -2075,15 +2079,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]);
@ -2130,13 +2138,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