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.
251// Add second grandparent to mempool252TryAddToMempool(pool, entry.FromTx(grandparent_tx_2));254// Only spends single dust out of two direct parents255BOOST_CHECK(!CheckEphemeralSpends({dust_non_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));256BOOST_CHECK(!child_state.IsValid());257BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_both_parents->GetWitnessHash());258child_state = TxValidationState();259child_wtxid = Wtxid();261// Spends both parents' dust262BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_wtxid));263BOOST_CHECK(child_state.IsValid());264BOOST_CHECK_EQUAL(child_wtxid, Wtxid());266// Now add dusty parent to mempool267TryAddToMempool(pool, entry.FromTx(parent_with_dust));269// Passes dust checks even with non-parent ancestors270BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_wtxid));271BOOST_CHECK(child_state.IsValid());272BOOST_CHECK_EQUAL(child_wtxid, Wtxid());273}275BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)276{277// Test TRUC policy helper functions278CTxMemPool& pool = *Assert(m_node.mempool);279LOCK2(cs_main, pool.cs);280TestMemPoolEntryHelper entry;281std::set<Txid> empty_conflicts_set;282std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> empty_parents;284auto mempool_tx_v3 = make_tx(random_outpoints(1), /*version=*/3);285TryAddToMempool(pool, entry.FromTx(mempool_tx_v3));286auto mempool_tx_v2 = make_tx(random_outpoints(1), /*version=*/2);287TryAddToMempool(pool, entry.FromTx(mempool_tx_v2));289// Cannot spend from an unconfirmed TRUC transaction unless this tx is also TRUC.290{291// mempool_tx_v3292// ^293// tx_v2_from_v3294auto tx_v2_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/2);295auto parents_v2_from_v3 = pool.GetParents(entry.FromTx(tx_v2_from_v3));296const auto expected_error_str{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",297tx_v2_from_v3->GetHash().ToString(), tx_v2_from_v3->GetWitnessHash().ToString(),298mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};299auto result_v2_from_v3{SingleTRUCChecks(pool, tx_v2_from_v3, parents_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))};300BOOST_CHECK_EQUAL(result_v2_from_v3->first, expected_error_str);301BOOST_CHECK_EQUAL(result_v2_from_v3->second, nullptr);303Package package_v3_v2{mempool_tx_v3, tx_v2_from_v3};304BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_parents), expected_error_str);305BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), {tx_v2_from_v3}, parents_v2_from_v3), expected_error_str);307// mempool_tx_v3 mempool_tx_v2308// ^ ^309// tx_v2_from_v2_and_v3310auto tx_v2_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);311auto parents_2_from_both{pool.GetParents(entry.FromTx(tx_v2_from_v2_and_v3))};312const auto expected_error_str_2{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",313tx_v2_from_v2_and_v3->GetHash().ToString(), tx_v2_from_v2_and_v3->GetWitnessHash().ToString(),314mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};315auto result_v2_from_both{SingleTRUCChecks(pool, tx_v2_from_v2_and_v3, parents_2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))};316BOOST_CHECK_EQUAL(result_v2_from_both->first, expected_error_str_2);317BOOST_CHECK_EQUAL(result_v2_from_both->second, nullptr);319Package package_v3_v2_v2{mempool_tx_v3, mempool_tx_v2, tx_v2_from_v2_and_v3};320BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v2_and_v3, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3), package_v3_v2_v2, empty_parents), expected_error_str_2);321}323// TRUC cannot spend from an unconfirmed non-TRUC transaction.324{325// mempool_tx_v2326// ^327// tx_v3_from_v2328auto tx_v3_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);329auto parents_v3_from_v2{pool.GetParents(entry.FromTx(tx_v3_from_v2))};330const auto expected_error_str{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",331tx_v3_from_v2->GetHash().ToString(), tx_v3_from_v2->GetWitnessHash().ToString(),332mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};333auto result_v3_from_v2{SingleTRUCChecks(pool, tx_v3_from_v2, parents_v3_from_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))};334BOOST_CHECK_EQUAL(result_v3_from_v2->first, expected_error_str);335BOOST_CHECK_EQUAL(result_v3_from_v2->second, nullptr);337Package package_v2_v3{mempool_tx_v2, tx_v3_from_v2};338BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_parents), expected_error_str);339BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), {tx_v3_from_v2}, parents_v3_from_v2), expected_error_str);341// mempool_tx_v3 mempool_tx_v2342// ^ ^343// tx_v3_from_v2_and_v3344auto tx_v3_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);345auto parents_v3_from_both{pool.GetParents(entry.FromTx(tx_v3_from_v2_and_v3))};346const auto expected_error_str_2{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",347tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString(),348mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};349auto result_v3_from_both{SingleTRUCChecks(pool, tx_v3_from_v2_and_v3, parents_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))};350BOOST_CHECK_EQUAL(result_v3_from_both->first, expected_error_str_2);351BOOST_CHECK_EQUAL(result_v3_from_both->second, nullptr);353// tx_v3_from_v2_and_v3 also violates TRUC_ANCESTOR_LIMIT.354const auto expected_error_str_3{strprintf("tx %s (wtxid=%s) would have too many ancestors",355tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString())};356Package package_v3_v2_v3{mempool_tx_v3, mempool_tx_v2, tx_v3_from_v2_and_v3};357BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2_and_v3, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3), package_v3_v2_v3, empty_parents), expected_error_str_3);358}359// V3 from V3 is ok, and non-V3 from non-V3 is ok.360{361// mempool_tx_v3362// ^363// tx_v3_from_v3364auto tx_v3_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);365auto parents_v3{pool.GetParents(entry.FromTx(tx_v3_from_v3))};366BOOST_CHECK(SingleTRUCChecks(pool, tx_v3_from_v3, parents_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v3))367== std::nullopt);369Package package_v3_v3{mempool_tx_v3, tx_v3_from_v3};370BOOST_CHECK(PackageTRUCChecks(pool, tx_v3_from_v3, GetVirtualTransactionSize(*tx_v3_from_v3), package_v3_v3, empty_parents) == std::nullopt);372// mempool_tx_v2373// ^374// tx_v2_from_v2375auto tx_v2_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);376auto parents_v2{pool.GetParents(entry.FromTx(tx_v2_from_v2))};377BOOST_CHECK(SingleTRUCChecks(pool, tx_v2_from_v2, parents_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2))378== std::nullopt);380Package package_v2_v2{mempool_tx_v2, tx_v2_from_v2};381BOOST_CHECK(PackageTRUCChecks(pool, tx_v2_from_v2, GetVirtualTransactionSize(*tx_v2_from_v2), package_v2_v2, empty_parents) == std::nullopt);382}384// Tx spending TRUC cannot have too many mempool ancestors385// Configuration where the tx has multiple direct parents.386{387Package package_multi_parents;388std::vector<COutPoint> mempool_outpoints;389mempool_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);390package_multi_parents.emplace_back(mempool_tx_v3);391for (size_t i{0}; i < 2; ++i) {392auto mempool_tx = make_tx(random_outpoints(i + 1), /*version=*/3);393TryAddToMempool(pool, entry.FromTx(mempool_tx));394mempool_outpoints.emplace_back(mempool_tx->GetHash(), 0);395package_multi_parents.emplace_back(mempool_tx);396}397auto tx_v3_multi_parent = make_tx(mempool_outpoints, /*version=*/3);398package_multi_parents.emplace_back(tx_v3_multi_parent);399auto parents{pool.GetParents(entry.FromTx(tx_v3_multi_parent))};400BOOST_CHECK_EQUAL(parents.size(), 3);401const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",402tx_v3_multi_parent->GetHash().ToString(), tx_v3_multi_parent->GetWitnessHash().ToString())};403auto result{SingleTRUCChecks(pool, tx_v3_multi_parent, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))};404BOOST_CHECK_EQUAL(result->first, expected_error_str);405BOOST_CHECK_EQUAL(result->second, nullptr);407BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_parents),408expected_error_str);409}411// Configuration where the tx is in a multi-generation chain.412{413Package package_multi_gen;414CTransactionRef middle_tx;415auto last_outpoint{random_outpoints(1)[0]};416for (size_t i{0}; i < 2; ++i) {417auto mempool_tx = make_tx({last_outpoint}, /*version=*/3);418TryAddToMempool(pool, entry.FromTx(mempool_tx));419last_outpoint = COutPoint{mempool_tx->GetHash(), 0};420package_multi_gen.emplace_back(mempool_tx);421if (i == 1) middle_tx = mempool_tx;422}423auto tx_v3_multi_gen = make_tx({last_outpoint}, /*version=*/3);424package_multi_gen.emplace_back(tx_v3_multi_gen);425auto parents{pool.GetParents(entry.FromTx(tx_v3_multi_gen))};426const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",427tx_v3_multi_gen->GetHash().ToString(), tx_v3_multi_gen->GetWitnessHash().ToString())};428auto result{SingleTRUCChecks(pool, tx_v3_multi_gen, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))};429BOOST_CHECK_EQUAL(result->first, expected_error_str);430BOOST_CHECK_EQUAL(result->second, nullptr);432// Middle tx is what triggers a failure for the grandchild:433BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_parents), expected_error_str);434BOOST_CHECK(PackageTRUCChecks(pool, tx_v3_multi_gen, GetVirtualTransactionSize(*tx_v3_multi_gen), package_multi_gen, empty_parents) == std::nullopt);435}437// Tx spending TRUC cannot be too large in virtual size.438auto many_inputs{random_outpoints(100)};439many_inputs.emplace_back(mempool_tx_v3->GetHash(), 0);440{441auto tx_v3_child_big = make_tx(many_inputs, /*version=*/3);442const auto vsize{GetVirtualTransactionSize(*tx_v3_child_big)};443auto parents{pool.GetParents(entry.FromTx(tx_v3_child_big))};444const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",445tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE)};446auto result{SingleTRUCChecks(pool, tx_v3_child_big, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))};447BOOST_CHECK_EQUAL(result->first, expected_error_str);448BOOST_CHECK_EQUAL(result->second, nullptr);450Package package_child_big{mempool_tx_v3, tx_v3_child_big};451BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_parents),452expected_error_str);453}455// Tx spending TRUC cannot have too many sigops.456// This child has 10 P2WSH multisig inputs.457auto multisig_outpoints{random_outpoints(10)};458multisig_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);459auto keys{random_keys(2)};460CScript script_multisig;461script_multisig << OP_1;462for (const auto& key : keys) {463script_multisig << ToByteVector(key);464}465script_multisig << OP_2 << OP_CHECKMULTISIG;466{467CMutableTransaction mtx_many_sigops = CMutableTransaction{};468mtx_many_sigops.version = TRUC_VERSION;469for (const auto& outpoint : multisig_outpoints) {470mtx_many_sigops.vin.emplace_back(outpoint);471mtx_many_sigops.vin.back().scriptWitness.stack.emplace_back(script_multisig.begin(), script_multisig.end());472}473mtx_many_sigops.vout.resize(1);474mtx_many_sigops.vout.back().scriptPubKey = CScript() << OP_TRUE;475mtx_many_sigops.vout.back().nValue = 10000;476auto tx_many_sigops{MakeTransactionRef(mtx_many_sigops)};478auto parents{pool.GetParents(entry.FromTx(tx_many_sigops))};479// legacy uses fAccurate = false, and the maximum number of multisig keys is used480const int64_t total_sigops{static_cast<int64_t>(tx_many_sigops->vin.size()) * static_cast<int64_t>(script_multisig.GetSigOpCount(/*fAccurate=*/false))};481BOOST_CHECK_EQUAL(total_sigops, tx_many_sigops->vin.size() * MAX_PUBKEYS_PER_MULTISIG);482const int64_t bip141_vsize{GetVirtualTransactionSize(*tx_many_sigops)};483// Weight limit is not reached...484BOOST_CHECK(SingleTRUCChecks(pool, tx_many_sigops, parents, empty_conflicts_set, bip141_vsize) == std::nullopt);485// ...but sigop limit is.486const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",487tx_many_sigops->GetHash().ToString(), tx_many_sigops->GetWitnessHash().ToString(),488total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, TRUC_CHILD_MAX_VSIZE)};489auto result{SingleTRUCChecks(pool, tx_many_sigops, parents, empty_conflicts_set,490GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP))};491BOOST_CHECK_EQUAL(result->first, expected_error_str);492BOOST_CHECK_EQUAL(result->second, nullptr);494Package package_child_sigops{mempool_tx_v3, tx_many_sigops};495BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_many_sigops, total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, package_child_sigops, empty_parents),496expected_error_str);497}499// Parent + child with TRUC in the mempool. Child is allowed as long as it is under TRUC_CHILD_MAX_VSIZE.500auto tx_mempool_v3_child = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
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.