Skip to content

Commit b8c79a0

Browse files
committed
Precompute sighashes
Original version by Nicolas Dorier. Precomputing version by Pieter Wuille.
1 parent a987431 commit b8c79a0

File tree

9 files changed

+163
-40
lines changed

9 files changed

+163
-40
lines changed

src/main.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,12 +1497,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
14971497

14981498
// Check against previous transactions
14991499
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
1500-
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true)) {
1500+
PrecomputedTransactionData txdata(tx);
1501+
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, txdata)) {
15011502
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
15021503
// need to turn both off, and compare against just turning off CLEANSTACK
15031504
// to see if the failure is specifically due to witness validation.
1504-
if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) &&
1505-
!CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true)) {
1505+
if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) &&
1506+
!CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) {
15061507
// Only the witness is wrong, so the transaction itself may be fine.
15071508
state.SetCorruptionPossible();
15081509
}
@@ -1518,7 +1519,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
15181519
// There is a similar check in CreateNewBlock() to prevent creating
15191520
// invalid blocks, however allowing such transactions into the mempool
15201521
// can be exploited as a DoS attack.
1521-
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
1522+
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata))
15221523
{
15231524
return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s",
15241525
__func__, hash.ToString(), FormatStateMessage(state));
@@ -1915,7 +1916,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
19151916
bool CScriptCheck::operator()() {
19161917
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
19171918
const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL;
1918-
if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore), &error)) {
1919+
if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error)) {
19191920
return false;
19201921
}
19211922
return true;
@@ -1974,7 +1975,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
19741975
}
19751976
}// namespace Consensus
19761977

1977-
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks)
1978+
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
19781979
{
19791980
if (!tx.IsCoinBase())
19801981
{
@@ -2001,7 +2002,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
20012002
assert(coins);
20022003

20032004
// Verify signature
2004-
CScriptCheck check(*coins, tx, i, flags, cacheStore);
2005+
CScriptCheck check(*coins, tx, i, flags, cacheStore, &txdata);
20052006
if (pvChecks) {
20062007
pvChecks->push_back(CScriptCheck());
20072008
check.swap(pvChecks->back());
@@ -2014,7 +2015,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
20142015
// avoid splitting the network between upgraded and
20152016
// non-upgraded nodes.
20162017
CScriptCheck check2(*coins, tx, i,
2017-
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore);
2018+
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, &txdata);
20182019
if (check2())
20192020
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
20202021
}
@@ -2412,6 +2413,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
24122413
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
24132414
vPos.reserve(block.vtx.size());
24142415
blockundo.vtxundo.reserve(block.vtx.size() - 1);
2416+
std::vector<PrecomputedTransactionData> txdata;
2417+
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
24152418
for (unsigned int i = 0; i < block.vtx.size(); i++)
24162419
{
24172420
const CTransaction &tx = block.vtx[i];
@@ -2458,13 +2461,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
24582461
return state.DoS(100, error("ConnectBlock(): too many sigops"),
24592462
REJECT_INVALID, "bad-blk-sigops");
24602463

2464+
txdata.emplace_back(tx);
24612465
if (!tx.IsCoinBase())
24622466
{
24632467
nFees += view.GetValueIn(tx)-tx.GetValueOut();
24642468

24652469
std::vector<CScriptCheck> vChecks;
24662470
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
2467-
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, nScriptCheckThreads ? &vChecks : NULL))
2471+
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : NULL))
24682472
return error("ConnectBlock(): CheckInputs on %s failed with %s",
24692473
tx.GetHash().ToString(), FormatStateMessage(state));
24702474
control.Add(vChecks);

src/main.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class CTxMemPool;
3939
class CValidationInterface;
4040
class CValidationState;
4141

42+
struct PrecomputedTransactionData;
4243
struct CNodeStateStats;
4344
struct LockPoints;
4445

@@ -347,7 +348,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
347348
* instead of being performed inline.
348349
*/
349350
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks,
350-
unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks = NULL);
351+
unsigned int flags, bool cacheStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = NULL);
351352

352353
/** Apply the effects of this transaction on the UTXO set represented by view */
353354
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
@@ -408,12 +409,13 @@ class CScriptCheck
408409
unsigned int nFlags;
409410
bool cacheStore;
410411
ScriptError error;
412+
PrecomputedTransactionData *txdata;
411413

412414
public:
413415
CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {}
414-
CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) :
416+
CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) :
415417
scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue),
416-
ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) { }
418+
ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { }
417419

418420
bool operator()();
419421

@@ -425,6 +427,7 @@ class CScriptCheck
425427
std::swap(nFlags, check.nFlags);
426428
std::swap(cacheStore, check.cacheStore);
427429
std::swap(error, check.error);
430+
std::swap(txdata, check.txdata);
428431
}
429432

430433
ScriptError GetScriptError() const { return error; }

src/script/bitcoinconsensus.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
8484

8585
// Regardless of the verification result, the tx did not error.
8686
set_error(err, bitcoinconsensus_ERR_OK);
87-
88-
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount), NULL);
87+
PrecomputedTransactionData txdata(tx);
88+
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata), NULL);
8989
} catch (const std::exception&) {
9090
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
9191
}

src/script/interpreter.cpp

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,37 +1108,57 @@ class CTransactionSignatureSerializer {
11081108
}
11091109
};
11101110

1111+
uint256 GetPrevoutHash(const CTransaction& txTo) {
1112+
CHashWriter ss(SER_GETHASH, 0);
1113+
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
1114+
ss << txTo.vin[n].prevout;
1115+
}
1116+
return ss.GetHash();
1117+
}
1118+
1119+
uint256 GetSequenceHash(const CTransaction& txTo) {
1120+
CHashWriter ss(SER_GETHASH, 0);
1121+
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
1122+
ss << txTo.vin[n].nSequence;
1123+
}
1124+
return ss.GetHash();
1125+
}
1126+
1127+
uint256 GetOutputsHash(const CTransaction& txTo) {
1128+
CHashWriter ss(SER_GETHASH, 0);
1129+
for (unsigned int n = 0; n < txTo.vout.size(); n++) {
1130+
ss << txTo.vout[n];
1131+
}
1132+
return ss.GetHash();
1133+
}
1134+
11111135
} // anon namespace
11121136

1113-
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion)
1137+
PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo)
1138+
{
1139+
hashPrevouts = GetPrevoutHash(txTo);
1140+
hashSequence = GetSequenceHash(txTo);
1141+
hashOutputs = GetOutputsHash(txTo);
1142+
}
1143+
1144+
uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache)
11141145
{
11151146
if (sigversion == SIGVERSION_WITNESS_V0) {
11161147
uint256 hashPrevouts;
11171148
uint256 hashSequence;
11181149
uint256 hashOutputs;
11191150

11201151
if (!(nHashType & SIGHASH_ANYONECANPAY)) {
1121-
CHashWriter ss(SER_GETHASH, 0);
1122-
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
1123-
ss << txTo.vin[n].prevout;
1124-
}
1125-
hashPrevouts = ss.GetHash(); // TODO: cache this value for all signatures in a transaction
1152+
hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo);
11261153
}
11271154

11281155
if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
1129-
CHashWriter ss(SER_GETHASH, 0);
1130-
for (unsigned int n = 0; n < txTo.vin.size(); n++) {
1131-
ss << txTo.vin[n].nSequence;
1132-
}
1133-
hashSequence = ss.GetHash(); // TODO: cache this value for all signatures in a transaction
1156+
hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo);
11341157
}
11351158

1159+
11361160
if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) {
1137-
CHashWriter ss(SER_GETHASH, 0);
1138-
for (unsigned int n = 0; n < txTo.vout.size(); n++) {
1139-
ss << txTo.vout[n];
1140-
}
1141-
hashOutputs = ss.GetHash(); // TODO: cache this value for all signatures in a transaction
1161+
hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo);
11421162
} else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) {
11431163
CHashWriter ss(SER_GETHASH, 0);
11441164
ss << txTo.vout[nIn];
@@ -1209,7 +1229,7 @@ bool TransactionSignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn
12091229
int nHashType = vchSig.back();
12101230
vchSig.pop_back();
12111231

1212-
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion);
1232+
uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);
12131233

12141234
if (!VerifySignature(vchSig, pubkey, sighash))
12151235
return false;

src/script/interpreter.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,20 @@ enum
9898

9999
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
100100

101+
struct PrecomputedTransactionData
102+
{
103+
uint256 hashPrevouts, hashSequence, hashOutputs;
104+
105+
PrecomputedTransactionData(const CTransaction& tx);
106+
};
107+
101108
enum SigVersion
102109
{
103110
SIGVERSION_BASE = 0,
104111
SIGVERSION_WITNESS_V0 = 1,
105112
};
106113

107-
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion);
114+
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = NULL);
108115

109116
class BaseSignatureChecker
110117
{
@@ -133,12 +140,14 @@ class TransactionSignatureChecker : public BaseSignatureChecker
133140
const CTransaction* txTo;
134141
unsigned int nIn;
135142
const CAmount amount;
143+
const PrecomputedTransactionData* txdata;
136144

137145
protected:
138146
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
139147

140148
public:
141-
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn) {}
149+
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {}
150+
TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
142151
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const;
143152
bool CheckLockTime(const CScriptNum& nLockTime) const;
144153
bool CheckSequence(const CScriptNum& nSequence) const;

src/script/sigcache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker
2222
bool store;
2323

2424
public:
25-
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nInIn, amount), store(storeIn) {}
25+
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amount, txdataIn), store(storeIn) {}
2626

2727
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
2828
};

src/test/script_P2SH_tests.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,20 @@ BOOST_AUTO_TEST_CASE(sign)
107107
}
108108
// All of the above should be OK, and the txTos have valid signatures
109109
// Check to make sure signature verification fails if we use the wrong ScriptSig:
110-
for (int i = 0; i < 8; i++)
110+
for (int i = 0; i < 8; i++) {
111+
PrecomputedTransactionData txdata(txTo[i]);
111112
for (int j = 0; j < 8; j++)
112113
{
113114
CScript sigSave = txTo[i].vin[0].scriptSig;
114115
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
115-
bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false)();
116+
bool sigOK = CScriptCheck(CCoins(txFrom, 0), txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
116117
if (i == j)
117118
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
118119
else
119120
BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));
120121
txTo[i].vin[0].scriptSig = sigSave;
121122
}
123+
}
122124
}
123125

124126
BOOST_AUTO_TEST_CASE(norecurse)

0 commit comments

Comments
 (0)