Skip to content

Commit e16718a

Browse files
author
MarcoFalke
committed
Merge #18401: Refactor: Initialize PrecomputedTransactionData in CheckInputScripts
f63dec1 [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 f63dec1 theStack: re-ACK f63dec1 fjahr: Re-ACK f63dec1 ariard: Code Review ACK f63dec1 Tree-SHA512: ecf9154077824ae4c274b4341e985797f3648c0cb0c31cb25ce382163b923a3acbc7048683720be4ae3663501801129cd0f48c441a36f049cc304ebe9f30994e
2 parents 5447097 + f63dec1 commit e16718a

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
@@ -1291,18 +1291,29 @@ uint256 GetOutputsHash(const T& txTo)
12911291
} // namespace
12921292

12931293
template <class T>
1294-
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
1294+
void PrecomputedTransactionData::Init(const T& txTo)
12951295
{
1296+
assert(!m_ready);
1297+
12961298
// Cache is calculated only for transactions with witness
12971299
if (txTo.HasWitness()) {
12981300
hashPrevouts = GetPrevoutHash(txTo);
12991301
hashSequence = GetSequenceHash(txTo);
13001302
hashOutputs = GetOutputsHash(txTo);
1301-
ready = true;
13021303
}
1304+
1305+
m_ready = true;
1306+
}
1307+
1308+
template <class T>
1309+
PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo)
1310+
{
1311+
Init(txTo);
13031312
}
13041313

13051314
// explicit instantiation
1315+
template void PrecomputedTransactionData::Init(const CTransaction& txTo);
1316+
template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo);
13061317
template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo);
13071318
template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo);
13081319

@@ -1315,7 +1326,7 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
13151326
uint256 hashPrevouts;
13161327
uint256 hashSequence;
13171328
uint256 hashOutputs;
1318-
const bool cacheready = cache && cache->ready;
1329+
const bool cacheready = cache && cache->m_ready;
13191330

13201331
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
13211332
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

@@ -1516,6 +1516,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
15161516
return true;
15171517
}
15181518

1519+
if (!txdata.m_ready) {
1520+
txdata.Init(tx);
1521+
}
1522+
15191523
for (unsigned int i = 0; i < tx.vin.size(); i++) {
15201524
const COutPoint &prevout = tx.vin[i].prevout;
15211525
const Coin& coin = inputs.AccessCoin(prevout);
@@ -2079,15 +2083,19 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
20792083

20802084
CBlockUndo blockundo;
20812085

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

20842094
std::vector<int> prevheights;
20852095
CAmount nFees = 0;
20862096
int nInputs = 0;
20872097
int64_t nSigOpsCost = 0;
20882098
blockundo.vtxundo.reserve(block.vtx.size() - 1);
2089-
std::vector<PrecomputedTransactionData> txdata;
2090-
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
20912099
for (unsigned int i = 0; i < block.vtx.size(); i++)
20922100
{
20932101
const CTransaction &tx = *(block.vtx[i]);
@@ -2134,13 +2142,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
21342142
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops");
21352143
}
21362144

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

0 commit comments

Comments
 (0)