Skip to content

Commit

Permalink
txhash: Provide TxHashCache to required contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenroose committed Mar 28, 2024
1 parent 11bb08f commit d7eac66
Show file tree
Hide file tree
Showing 16 changed files with 59 additions and 27 deletions.
5 changes: 4 additions & 1 deletion src/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,12 @@ bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned
return false;
}

TxHashCache txhash_cache;
if (txdata) {
return VerifyScript(input.final_script_sig, utxo.scriptPubKey, &input.final_script_witness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, *txdata, MissingDataBehavior::FAIL});
return VerifyScript(input.final_script_sig, utxo.scriptPubKey, &input.final_script_witness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, *txdata, &txhash_cache, MissingDataBehavior::FAIL});
} else {
//TODO(stevenroose) I cant pass in txhash_cache here without passing in a non-null txdata..
//should I create a new constructor? is it fine to not have txhashcache if there's no txdata?
return VerifyScript(input.final_script_sig, utxo.scriptPubKey, &input.final_script_witness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, MissingDataBehavior::FAIL});
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/script/bitcoinconsensus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <pubkey.h>
#include <script/interpreter.h>
#include <version.h>
#include <script/txhash.h>

namespace {

Expand Down Expand Up @@ -89,7 +90,8 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP
set_error(err, bitcoinconsensus_ERR_OK);

PrecomputedTransactionData txdata(tx);
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata, MissingDataBehavior::FAIL), nullptr);
TxHashCache txhash_cache;
return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, txdata, &txhash_cache, MissingDataBehavior::FAIL), nullptr);
} catch (const std::exception&) {
return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing
}
Expand Down
4 changes: 2 additions & 2 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
virtual bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const;

public:
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(nullptr), txhash_cache(nullptr) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn, TxHashCache* txhashCacheIn, MissingDataBehavior mdb) : txTo(txToIn), m_mdb(mdb), nIn(nInIn), amount(amountIn), txdata(&txdataIn), txhash_cache(txhashCacheIn) {}
bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
bool CheckSchnorrSignature(Span<const unsigned char> sig, KeyVersion pubkeyver, Span<const unsigned char> pubkey, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
Expand Down
3 changes: 2 additions & 1 deletion src/script/sigcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define BITCOIN_SCRIPT_SIGCACHE_H

#include <script/interpreter.h>
#include <script/txhash.h>
#include <span.h>
#include <util/hasher.h>

Expand All @@ -26,7 +27,7 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker
bool store;

public:
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn) {}
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn, TxHashCache* txhashCacheIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn, txhashCacheIn, MissingDataBehavior::ASSERT_FAIL), store(storeIn) {}

bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
bool VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
Expand Down
7 changes: 5 additions & 2 deletions src/script/sign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <script/miniscript.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <script/txhash.h>
#include <uint256.h>
#include <util/translation.h>
#include <util/vector.h>
Expand All @@ -25,9 +26,10 @@ MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMu
{
}

//TODO(stevenroose) Should the Creator (i.e. it's Checker) have a TxHashCache??
MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction& tx, unsigned int input_idx, const CAmount& amount, const PrecomputedTransactionData* txdata, int hash_type)
: m_txto{tx}, nIn{input_idx}, nHashType{hash_type}, amount{amount},
checker{txdata ? MutableTransactionSignatureChecker{&m_txto, nIn, amount, *txdata, MissingDataBehavior::FAIL} :
checker{txdata ? MutableTransactionSignatureChecker{&m_txto, nIn, amount, *txdata, nullptr, MissingDataBehavior::FAIL} :
MutableTransactionSignatureChecker{&m_txto, nIn, amount, MissingDataBehavior::FAIL}},
m_txdata(txdata)
{
Expand Down Expand Up @@ -764,6 +766,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
const CTransaction txConst(mtx);

PrecomputedTransactionData txdata;
TxHashCache txhash_cache;
std::vector<CTxOut> spent_outputs;
for (unsigned int i = 0; i < mtx.vin.size(); ++i) {
CTxIn& txin = mtx.vin[i];
Expand Down Expand Up @@ -805,7 +808,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
}

ScriptError serror = SCRIPT_ERR_OK;
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, MissingDataBehavior::FAIL), &serror)) {
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, &txhash_cache, MissingDataBehavior::FAIL), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
input_errors[i] = Untranslated("Unable to sign input, invalid stack size (possibly missing key)");
Expand Down
4 changes: 3 additions & 1 deletion src/signet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <hash.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/txhash.h>
#include <span.h>
#include <script/interpreter.h>
#include <script/standard.h>
Expand Down Expand Up @@ -140,8 +141,9 @@ bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& cons
const CScriptWitness& witness = signet_txs->m_to_sign.vin[0].scriptWitness;

PrecomputedTransactionData txdata;
TxHashCache txhash_cache; //TODO(stevenroose) can we pass nullptr here because signet block checks shouldn't do txhash?
txdata.Init(signet_txs->m_to_sign, {signet_txs->m_to_spend.vout[0]});
TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /* nInIn= */ 0, /* amountIn= */ signet_txs->m_to_spend.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
TransactionSignatureChecker sigcheck(&signet_txs->m_to_sign, /* nInIn= */ 0, /* amountIn= */ signet_txs->m_to_spend.vout[0].nValue, txdata, &txhash_cache, MissingDataBehavior::ASSERT_FAIL);

if (!VerifyScript(scriptSig, signet_txs->m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution invalid)\n");
Expand Down
7 changes: 5 additions & 2 deletions src/test/fuzz/script_assets_test_minimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/interpreter.h>
#include <script/txhash.h>
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
Expand Down Expand Up @@ -159,8 +160,9 @@ void Test(const std::string& str)
tx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
PrecomputedTransactionData txdata;
TxHashCache txhash_cache;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata, &txhash_cache, MissingDataBehavior::ASSERT_FAIL);
for (const auto flags : ALL_FLAGS) {
// "final": true tests are valid for all flags. Others are only valid with flags that are
// a subset of test_flags.
Expand All @@ -174,8 +176,9 @@ void Test(const std::string& str)
tx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
PrecomputedTransactionData txdata;
TxHashCache txhash_cache;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata, &txhash_cache, MissingDataBehavior::ASSERT_FAIL);
for (const auto flags : ALL_FLAGS) {
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
if ((flags & test_flags) == test_flags) {
Expand Down
3 changes: 2 additions & 1 deletion src/test/fuzz/script_flags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ FUZZ_TARGET(script_flags)
spent_outputs.push_back(prevout);
}
PrecomputedTransactionData txdata;
TxHashCache txhash_cache;
txdata.Init(tx, std::move(spent_outputs));

for (unsigned i = 0; i < tx.vin.size(); ++i) {
const CTxOut& prevout = txdata.m_spent_outputs.at(i);
const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata, MissingDataBehavior::ASSERT_FAIL};
const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata, &txhash_cache, MissingDataBehavior::ASSERT_FAIL};

ScriptError serror;
const bool ret = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror);
Expand Down
3 changes: 2 additions & 1 deletion src/test/fuzz/script_sigcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ FUZZ_TARGET_INIT(script_sigcache, initialize_script_sigcache)
const CAmount amount = ConsumeMoney(fuzzed_data_provider);
const bool store = fuzzed_data_provider.ConsumeBool();
PrecomputedTransactionData tx_data;
CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
TxHashCache txhash_cache;
CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data, &txhash_cache};
if (fuzzed_data_provider.ConsumeBool()) {
const auto random_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(64);
const XOnlyPubKey pub_key(ConsumeUInt256(fuzzed_data_provider));
Expand Down
4 changes: 3 additions & 1 deletion src/test/script_p2sh_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <script/script_error.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/txhash.h>
#include <test/util/setup_common.h>
#include <validation.h>

Expand Down Expand Up @@ -109,11 +110,12 @@ BOOST_AUTO_TEST_CASE(sign)
// Check to make sure signature verification fails if we use the wrong ScriptSig:
for (int i = 0; i < 8; i++) {
PrecomputedTransactionData txdata(txTo[i]);
TxHashCache txhash_cache;
for (int j = 0; j < 8; j++)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)();
bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata, &txhash_cache)();
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
Expand Down
9 changes: 7 additions & 2 deletions src/test/script_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <script/sigcache.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/txhash.h>
#include <streams.h>
#include <test/util/json.h>
#include <test/util/random.h>
Expand Down Expand Up @@ -1685,8 +1686,10 @@ static void AssetTest(const UniValue& test)
mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
CTransaction tx(mtx);
PrecomputedTransactionData txdata;
TxHashCache txhash_cache;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata, &txhash_cache);

for (const auto flags : ALL_CONSENSUS_FLAGS) {
// "final": true tests are valid for all flags. Others are only valid with flags that are
// a subset of test_flags.
Expand All @@ -1702,8 +1705,10 @@ static void AssetTest(const UniValue& test)
mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
CTransaction tx(mtx);
PrecomputedTransactionData txdata;
TxHashCache txhash_cache;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata, &txhash_cache);

for (const auto flags : ALL_CONSENSUS_FLAGS) {
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
if ((flags & test_flags) == test_flags) {
Expand Down
12 changes: 8 additions & 4 deletions src/test/transaction_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <script/sign.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <script/txhash.h>
#include <streams.h>
#include <test/util/json.h>
#include <test/util/random.h>
Expand Down Expand Up @@ -79,16 +80,18 @@ bool CheckMapFlagNames()
*/
bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>& map_prevout_scriptPubKeys,
const std::map<COutPoint, int64_t>& map_prevout_values, unsigned int flags,
const PrecomputedTransactionData& txdata, const std::string& strTest, bool expect_valid)
const PrecomputedTransactionData& txdata,
const std::string& strTest, bool expect_valid)
{
bool tx_valid = true;
ScriptError err = expect_valid ? SCRIPT_ERR_UNKNOWN_ERROR : SCRIPT_ERR_OK;
TxHashCache txhash_cache;
for (unsigned int i = 0; i < tx.vin.size() && tx_valid; ++i) {
const CTxIn input = tx.vin[i];
const CAmount amount = map_prevout_values.count(input.prevout) ? map_prevout_values.at(input.prevout) : 0;
try {
tx_valid = VerifyScript(input.scriptSig, map_prevout_scriptPubKeys.at(input.prevout),
&input.scriptWitness, flags, TransactionSignatureChecker(&tx, i, amount, txdata, MissingDataBehavior::ASSERT_FAIL), &err);
&input.scriptWitness, flags, TransactionSignatureChecker(&tx, i, amount, txdata, &txhash_cache, MissingDataBehavior::ASSERT_FAIL), &err);
} catch (...) {
BOOST_ERROR("Bad test: " << strTest);
return true; // The test format is bad and an error is thrown. Return true to silence further error.
Expand Down Expand Up @@ -496,6 +499,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction)

// check all inputs concurrently, with the cache
PrecomputedTransactionData txdata(tx);
TxHashCache txhash_cache;
CCheckQueue<CScriptCheck> scriptcheckqueue(128);
CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue);

Expand All @@ -513,7 +517,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction)

for(uint32_t i = 0; i < mtx.vin.size(); i++) {
std::vector<CScriptCheck> vChecks;
vChecks.emplace_back(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
vChecks.emplace_back(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata, &txhash_cache);
control.Add(std::move(vChecks));
}

Expand All @@ -527,7 +531,7 @@ SignatureData CombineSignatures(const CMutableTransaction& input1, const CMutabl
SignatureData sigdata;
sigdata = DataFromTransaction(input1, 0, tx->vout[0]);
sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0]));
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(input1, 0, tx->vout[0].nValue, SIGHASH_ALL), tx->vout[0].scriptPubKey, sigdata);
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(input1, 0, tx->vout[0].nValue, nullptr, SIGHASH_ALL), tx->vout[0].scriptPubKey, sigdata);
return sigdata;
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/txvalidationcache_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)

// Sign
SignatureData sigdata;
BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(valid_with_witness_tx, 0, 11 * CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata));
BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(valid_with_witness_tx, 0, 11 * CENT, nullptr, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata));
UpdateInput(valid_with_witness_tx.vin[0], sigdata);

// This should be valid under all script flags.
Expand Down Expand Up @@ -356,7 +356,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
// Sign
for (int i = 0; i < 2; ++i) {
SignatureData sigdata;
BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(tx, i, 11 * CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata));
BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(tx, i, 11 * CENT, nullptr, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata));
UpdateInput(tx.vin[i], sigdata);
}

Expand Down
Loading

0 comments on commit d7eac66

Please sign in to comment.