Skip to content

Commit

Permalink
[REFACTOR] Initialize PrecomputedTransactionData in CheckInputScripts
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sipa authored and jnewbery committed Apr 12, 2020
1 parent 5504703 commit f63dec1
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 9 deletions.
17 changes: 14 additions & 3 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1279,18 +1279,29 @@ uint256 GetOutputsHash(const T& txTo)
} // namespace

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
if (txTo.HasWitness()) {
hashPrevouts = GetPrevoutHash(txTo);
hashSequence = GetSequenceHash(txTo);
hashOutputs = GetOutputsHash(txTo);
ready = true;
}

m_ready = true;
}

template <class T>
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
{
Init(txTo);
}

// 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 CMutableTransaction& txTo);

Expand All @@ -1303,7 +1314,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
uint256 hashPrevouts;
uint256 hashSequence;
uint256 hashOutputs;
const bool cacheready = cache && cache->ready;
const bool cacheready = cache && cache->m_ready;

if (!(nHashType & SIGHASH_ANYONECANPAY)) {
hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);
Expand Down
7 changes: 6 additions & 1 deletion src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,12 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
struct PrecomputedTransactionData
{
uint256 hashPrevouts, hashSequence, hashOutputs;
bool ready = false;
bool m_ready = false;

PrecomputedTransactionData() = default;

template <class T>
void Init(const T& tx);

template <class T>
explicit PrecomputedTransactionData(const T& tx);
Expand Down
17 changes: 12 additions & 5 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
// scripts (ie, other policy checks pass). We perform the inexpensive
// checks first and avoid hashing and signature verification unless those
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
PrecomputedTransactionData txdata(*ptx);
PrecomputedTransactionData txdata;

if (!PolicyScriptChecks(args, workspace, txdata)) return false;

Expand Down Expand Up @@ -1512,6 +1512,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
return true;
}

if (!txdata.m_ready) {
txdata.Init(tx);
}

for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout);
Expand Down Expand Up @@ -2075,15 +2079,19 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,

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);
std::vector<PrecomputedTransactionData> txsdata(block.vtx.size());

std::vector<int> prevheights;
CAmount nFees = 0;
int nInputs = 0;
int64_t nSigOpsCost = 0;
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++)
{
const CTransaction &tx = *(block.vtx[i]);
Expand Down Expand Up @@ -2130,13 +2138,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops");
}

txdata.emplace_back(tx);
if (!tx.IsCoinBase())
{
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
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
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
Expand Down

0 comments on commit f63dec1

Please sign in to comment.