Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2011-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <consensus/validation.h>6#include <key.h>7#include <random.h>8#include <script/sigcache.h>9#include <script/sign.h>10#include <script/signingprovider.h>11#include <test/util/setup_common.h>12#include <txmempool.h>13#include <util/chaintype.h>14#include <validation.h>16#include <boost/test/unit_test.hpp>18struct Dersig100Setup : public TestChain100Setup {19Dersig100Setup()20: TestChain100Setup{ChainType::REGTEST, {.extra_args = {"-testactivationheight=dersig@102"}}} {}21};23bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,24const CCoinsViewCache& inputs, script_verify_flags flags, bool cacheSigStore,25bool cacheFullScriptStore, PrecomputedTransactionData& txdata,26ValidationCache& validation_cache,27std::vector<CScriptCheck>* pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main);29BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)31BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)32{33// Make sure skipping validation of transactions that were34// validated going into the memory pool does not allow35// double-spends in blocks to pass validation when they should not.37CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;39const auto ToMemPool = [this](const CMutableTransaction& tx) {40LOCK(cs_main);42const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(tx));43return result.m_result_type == MempoolAcceptResult::ResultType::VALID;44};46// Create a double-spend of mature coinbase txn:47std::vector<CMutableTransaction> spends;48spends.resize(2);49for (int i = 0; i < 2; i++)50{51spends[i].version = 1;52spends[i].vin = {CTxIn{m_coinbase_txns[0]->GetHash(), 0}};53spends[i].vout = {CTxOut{11*CENT, scriptPubKey}};55// Sign:56std::vector<unsigned char> vchSig;57uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SigVersion::BASE);58BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));59vchSig.push_back((unsigned char)SIGHASH_ALL);60spends[i].vin[0].scriptSig << vchSig;61}63CBlock block;65// Test 1: block with both of those transactions should be rejected.66block = CreateAndProcessBlock(spends, scriptPubKey);67{68LOCK(cs_main);69BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());70}72// Test 2: ... and should be rejected if spend1 is in the memory pool73BOOST_CHECK(ToMemPool(spends[0]));74block = CreateAndProcessBlock(spends, scriptPubKey);75{76LOCK(cs_main);77BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());78}79BOOST_CHECK_EQUAL(m_node.mempool->size(), 1U);80WITH_LOCK(m_node.mempool->cs, m_node.mempool->removeRecursive(CTransaction{spends[0]}, MemPoolRemovalReason::CONFLICT));81BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);83// Test 3: ... and should be rejected if spend2 is in the memory pool84BOOST_CHECK(ToMemPool(spends[1]));85block = CreateAndProcessBlock(spends, scriptPubKey);86{87LOCK(cs_main);88BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());89}90BOOST_CHECK_EQUAL(m_node.mempool->size(), 1U);91WITH_LOCK(m_node.mempool->cs, m_node.mempool->removeRecursive(CTransaction{spends[1]}, MemPoolRemovalReason::CONFLICT));92BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);94// Final sanity test: first spend in *m_node.mempool, second in block, that's OK:95std::vector<CMutableTransaction> oneSpend;96oneSpend.push_back(spends[0]);97BOOST_CHECK(ToMemPool(spends[1]));98block = CreateAndProcessBlock(oneSpend, scriptPubKey);99{100LOCK(cs_main);101BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());102}103// spends[1] should have been removed from the mempool when the104// block with spends[0] is accepted:105BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);106}108// Run CheckInputScripts (using CoinsTip()) on the given transaction, for all script109// flags. Test that CheckInputScripts passes for all flags that don't overlap with110// the failing_flags argument, but otherwise fails.111// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may112// get reassigned) have an interaction with DISCOURAGE_UPGRADABLE_NOPS: if113// the script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain114// CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain115// OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution116// should fail.117static void ValidateCheckInputsForAllFlags(const CTransaction &tx, script_verify_flags failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)118{119PrecomputedTransactionData txdata;121FastRandomContext insecure_rand(true);123for (int count = 0; count < 10000; ++count) {124TxValidationState state;126// Randomly selects flag combinations127script_verify_flags test_flags = script_verify_flags::from_int(insecure_rand.randrange(MAX_SCRIPT_VERIFY_FLAGS));129// Filter out incompatible flag choices130if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {131// CLEANSTACK requires P2SH and WITNESS, see VerifyScript() in132// script/interpreter.cpp133test_flags |= SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS;134}135if ((test_flags & SCRIPT_VERIFY_TAPROOT)) {136// TAPROOT requires WITNESS137test_flags |= SCRIPT_VERIFY_WITNESS;138}139if ((test_flags & SCRIPT_VERIFY_WITNESS)) {140// WITNESS requires P2SH141test_flags |= SCRIPT_VERIFY_P2SH;142}143bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, nullptr);144// CheckInputScripts should succeed iff test_flags doesn't intersect with145// failing_flags146bool expected_return_value = !(test_flags & failing_flags);147BOOST_CHECK_EQUAL(ret, expected_return_value);149// Test the caching150if (ret && add_to_cache) {151// Check that we get a cache hit if the tx was valid152std::vector<CScriptCheck> scriptchecks;153BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));154BOOST_CHECK(scriptchecks.empty());155} else {156// Check that we get script executions to check, if the transaction157// was invalid, or we didn't add to cache.158std::vector<CScriptCheck> scriptchecks;159BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, true, add_to_cache, txdata, validation_cache, &scriptchecks));160BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());161}162}163}165BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)166{167// Test that passing CheckInputScripts with one set of script flags doesn't imply168// that we would pass again with a different set of flags.169CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;170CScript p2sh_scriptPubKey = GetScriptForDestination(ScriptHash(p2pk_scriptPubKey));171CScript p2pkh_scriptPubKey = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));172CScript p2wpkh_scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(coinbaseKey.GetPubKey()));173CScript p2tr_scriptPubKey = GetScriptForDestination(WitnessV1Taproot(XOnlyPubKey(coinbaseKey.GetPubKey())));175FillableSigningProvider keystore;176BOOST_CHECK(keystore.AddKey(coinbaseKey));177BOOST_CHECK(keystore.AddCScript(p2pk_scriptPubKey));179// flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed pubkey thing181// Create 2 outputs that match the three scripts above, spending the first182// coinbase tx.183CMutableTransaction spend_tx;185spend_tx.version = 1;186spend_tx.vin = {CTxIn{m_coinbase_txns[0]->GetHash(), 0}};187spend_tx.vout = {188CTxOut{11*CENT, p2sh_scriptPubKey},189CTxOut{11*CENT, p2wpkh_scriptPubKey},190CTxOut{11*CENT, CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG},191CTxOut{11*CENT, CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG},192CTxOut{11*CENT, p2tr_scriptPubKey},193};195// Sign, with a non-DER signature196{197std::vector<unsigned char> vchSig;198uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);199BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));200vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER201vchSig.push_back((unsigned char)SIGHASH_ALL);202spend_tx.vin[0].scriptSig << vchSig;203}205// Test that invalidity under a set of flags doesn't preclude validity206// under other (eg consensus) flags.207// spend_tx is invalid according to DERSIG208{209LOCK(cs_main);211TxValidationState state;212PrecomputedTransactionData ptd_spend_tx;214BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, nullptr));216// If we call again asking for scriptchecks (as happens in217// ConnectBlock), we should add a script check object for this -- we're218// not caching invalidity (if that changes, delete this test case).219std::vector<CScriptCheck> scriptchecks;220BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, &scriptchecks));221BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);223// Test that CheckInputScripts returns true iff DERSIG-enforcing flags are224// not present. Don't add these checks to the cache, so that we can225// test later that block validation works fine in the absence of cached226// successes.227ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);228}230// And if we produce a block with this tx, it should be valid (DERSIG not231// enabled yet), even though there's no cache entry.232CBlock block;234block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);235LOCK(cs_main);236BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());237BOOST_CHECK(m_node.chainman->ActiveChainstate().CoinsTip().GetBestBlock() == block.GetHash());239// Test P2SH: construct a transaction that is valid without P2SH, and240// then test validity with P2SH.241{242CMutableTransaction invalid_under_p2sh_tx;243invalid_under_p2sh_tx.version = 1;244invalid_under_p2sh_tx.vin = {CTxIn{spend_tx.GetHash(), 0}};245invalid_under_p2sh_tx.vout = {CTxOut{11*CENT, p2pk_scriptPubKey}};246std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());247invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;249ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);250}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.