Skip to content

Commit f63dec1

Browse files
sipajnewbery
authored andcommitted
[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.
1 parent 5504703 commit f63dec1

File tree

3 files changed

+32
-9
lines changed

3 files changed

+32
-9
lines changed

src/script/interpreter.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,18 +1279,29 @@ uint256 GetOutputsHash(const T& txTo)
12791279
} // namespace
12801280

12811281
template <class T>
1282-
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
1282+
void PrecomputedTransactionData::Init(const T& txTo)
12831283
{
1284+
assert(!m_ready);
1285+
12841286
// Cache is calculated only for transactions with witness
12851287
if (txTo.HasWitness()) {
12861288
hashPrevouts = GetPrevoutHash(txTo);
12871289
hashSequence = GetSequenceHash(txTo);
12881290
hashOutputs = GetOutputsHash(txTo);
1289-
ready = true;
12901291
}
1292+
1293+
m_ready = true;
1294+
}
1295+
1296+
template <class T>
1297+
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
1298+
{
1299+
Init(txTo);
12911300
}
12921301

12931302
// explicit instantiation
1303+
template void PrecomputedTransactionData::Init(const CTransaction& txTo);
1304+
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo);
12941305
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
12951306
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
12961307

@@ -1303,7 +1314,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
13031314
uint256 hashPrevouts;
13041315
uint256 hashSequence;
13051316
uint256 hashOutputs;
1306-
const bool cacheready = cache && cache->ready;
1317+
const bool cacheready = cache && cache->m_ready;
13071318

13081319
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
13091320
hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo);

src/script/interpreter.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,12 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i
121121
struct PrecomputedTransactionData
122122
{
123123
uint256 hashPrevouts, hashSequence, hashOutputs;
124-
bool ready = false;
124+
bool m_ready = false;
125+
126+
PrecomputedTransactionData() = default;
127+
128+
template <class T>
129+
void Init(const T& tx);
125130

126131
template <class T>
127132
explicit PrecomputedTransactionData(const T& tx);

src/validation.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,7 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
10161016
// scripts (ie, other policy checks pass). We perform the inexpensive
10171017
// checks first and avoid hashing and signature verification unless those
10181018
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
1019-
PrecomputedTransactionData txdata(*ptx);
1019+
PrecomputedTransactionData txdata;
10201020

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

@@ -1512,6 +1512,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
15121512
return true;
15131513
}
15141514

1515+
if (!txdata.m_ready) {
1516+
txdata.Init(tx);
1517+
}
1518+
15151519
for (unsigned int i = 0; i < tx.vin.size(); i++) {
15161520
const COutPoint &prevout = tx.vin[i].prevout;
15171521
const Coin& coin = inputs.AccessCoin(prevout);
@@ -2075,15 +2079,19 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
20752079

20762080
CBlockUndo blockundo;
20772081

2082+
// Precomputed transaction data pointers must not be invalidated
2083+
// until after `control` has run the script checks (potentially
2084+
// in multiple threads). Preallocate the vector size so a new allocation
2085+
// doesn't invalidate pointers into the vector, and keep txsdata in scope
2086+
// for as long as `control`.
20782087
CCheckQueueControl<CScriptCheck> control(fScriptChecks && g_parallel_script_checks ? &scriptcheckqueue : nullptr);
2088+
std::vector<PrecomputedTransactionData> txsdata(block.vtx.size());
20792089

20802090
std::vector<int> prevheights;
20812091
CAmount nFees = 0;
20822092
int nInputs = 0;
20832093
int64_t nSigOpsCost = 0;
20842094
blockundo.vtxundo.reserve(block.vtx.size() - 1);
2085-
std::vector<PrecomputedTransactionData> txdata;
2086-
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
20872095
for (unsigned int i = 0; i < block.vtx.size(); i++)
20882096
{
20892097
const CTransaction &tx = *(block.vtx[i]);
@@ -2130,13 +2138,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
21302138
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops");
21312139
}
21322140

2133-
txdata.emplace_back(tx);
21342141
if (!tx.IsCoinBase())
21352142
{
21362143
std::vector<CScriptCheck> vChecks;
21372144
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
21382145
TxValidationState tx_state;
2139-
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
2146+
if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
21402147
// Any transaction validation failure in ConnectBlock is a block consensus failure
21412148
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
21422149
tx_state.GetRejectReason(), tx_state.GetDebugMessage());

0 commit comments

Comments
 (0)