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) 2021-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_io.h>7#include <policy/packages.h>8#include <policy/policy.h>9#include <policy/rbf.h>10#include <primitives/transaction.h>11#include <script/script.h>12#include <serialize.h>13#include <streams.h>14#include <test/util/common.h>15#include <test/util/random.h>16#include <test/util/script.h>17#include <test/util/setup_common.h>18#include <util/strencodings.h>19#include <test/util/txmempool.h>20#include <validation.h>22#include <boost/test/unit_test.hpp>24using namespace util::hex_literals;26// A fee amount that is above 1sat/vB but below 5sat/vB for most transactions created within these27// unit tests.28static const CAmount low_fee_amt{200};30struct TxPackageTest : TestChain100Setup {31// Create placeholder transactions that have no meaning.32inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs)33{34CMutableTransaction mtx = CMutableTransaction();35mtx.vin.resize(num_inputs);36mtx.vout.resize(num_outputs);37auto random_script = CScript() << ToByteVector(m_rng.rand256()) << ToByteVector(m_rng.rand256());38for (size_t i{0}; i < num_inputs; ++i) {39mtx.vin[i].prevout.hash = Txid::FromUint256(m_rng.rand256());40mtx.vin[i].prevout.n = 0;41mtx.vin[i].scriptSig = random_script;42}43for (size_t o{0}; o < num_outputs; ++o) {44mtx.vout[o].nValue = 1 * CENT;45mtx.vout[o].scriptPubKey = random_script;46}47return MakeTransactionRef(mtx);48}49}; // struct TxPackageTest51BOOST_FIXTURE_TEST_SUITE(txpackage_tests, TxPackageTest)53BOOST_AUTO_TEST_CASE(package_hash_tests)54{55// Random real segwit transaction56DataStream stream_1{57"02000000000101964b8aa63509579ca6086e6012eeaa4c2f4dd1e283da29b67c8eea38b3c6fd220000000000fdffffff0294c618000000000017a9145afbbb42f4e83312666d0697f9e66259912ecde38768fa2c0000000000160014897388a0889390fd0e153a22bb2cf9d8f019faf50247304402200547406380719f84d68cf4e96cc3e4a1688309ef475b150be2b471c70ea562aa02206d255f5acc40fd95981874d77201d2eb07883657ce1c796513f32b6079545cdf0121023ae77335cefcb5ab4c1dc1fb0d2acfece184e593727d7d5906c78e564c7c11d125cf0c00"_hex,58};59CTransaction tx_1(deserialize, TX_WITH_WITNESS, stream_1);60CTransactionRef ptx_1{MakeTransactionRef(tx_1)};62// Random real nonsegwit transaction63DataStream stream_2{64"01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"_hex,65};66CTransaction tx_2(deserialize, TX_WITH_WITNESS, stream_2);67CTransactionRef ptx_2{MakeTransactionRef(tx_2)};69// Random real segwit transaction70DataStream stream_3{71"0200000000010177862801f77c2c068a70372b4c435ef8dd621291c36a64eb4dd491f02218f5324600000000fdffffff014a0100000000000022512035ea312034cfac01e956a269f3bf147f569c2fbb00180677421262da042290d803402be713325ff285e66b0380f53f2fae0d0fb4e16f378a440fed51ce835061437566729d4883bc917632f3cff474d6384bc8b989961a1d730d4a87ed38ad28bd337b20f1d658c6c138b1c312e072b4446f50f01ae0da03a42e6274f8788aae53416a7fac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800357b2270223a226272632d3230222c226f70223a226d696e74222c227469636b223a224342414c222c22616d74223a2236393639227d6821c1f1d658c6c138b1c312e072b4446f50f01ae0da03a42e6274f8788aae53416a7f00000000"_hex,72};73CTransaction tx_3(deserialize, TX_WITH_WITNESS, stream_3);74CTransactionRef ptx_3{MakeTransactionRef(tx_3)};76// It's easy to see that wtxids are sorted in lexicographical order:77constexpr Wtxid wtxid_1{"85cd1a31eb38f74ed5742ec9cb546712ab5aaf747de28a9168b53e846cbda17f"};78constexpr Wtxid wtxid_2{"b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"};79constexpr Wtxid wtxid_3{"e065bac15f62bb4e761d761db928ddee65a47296b2b776785abb912cdec474e3"};80BOOST_CHECK_EQUAL(tx_1.GetWitnessHash(), wtxid_1);81BOOST_CHECK_EQUAL(tx_2.GetWitnessHash(), wtxid_2);82BOOST_CHECK_EQUAL(tx_3.GetWitnessHash(), wtxid_3);84BOOST_CHECK(wtxid_1.GetHex() < wtxid_2.GetHex());85BOOST_CHECK(wtxid_2.GetHex() < wtxid_3.GetHex());87// The txids are not (we want to test that sorting and hashing use wtxid, not txid):88constexpr Txid txid_1{"bd0f71c1d5e50589063e134fad22053cdae5ab2320db5bf5e540198b0b5a4e69"};89constexpr Txid txid_2{"b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"};90constexpr Txid txid_3{"ee707be5201160e32c4fc715bec227d1aeea5940fb4295605e7373edce3b1a93"};91BOOST_CHECK_EQUAL(tx_1.GetHash(), txid_1);92BOOST_CHECK_EQUAL(tx_2.GetHash(), txid_2);93BOOST_CHECK_EQUAL(tx_3.GetHash(), txid_3);95BOOST_CHECK(txid_2.GetHex() < txid_1.GetHex());97BOOST_CHECK(txid_1.ToUint256() != wtxid_1.ToUint256());98BOOST_CHECK(txid_2.ToUint256() == wtxid_2.ToUint256());99BOOST_CHECK(txid_3.ToUint256() != wtxid_3.ToUint256());101// We are testing that both functions compare using GetHex() and not uint256.102// (in this pair of wtxids, hex string order != uint256 order)103BOOST_CHECK(wtxid_2 < wtxid_1);104// (in this pair of wtxids, hex string order == uint256 order)105BOOST_CHECK(wtxid_2 < wtxid_3);107// All permutations of the package containing ptx_1, ptx_2, ptx_3 have the same package hash108std::vector<CTransactionRef> package_123{ptx_1, ptx_2, ptx_3};109std::vector<CTransactionRef> package_132{ptx_1, ptx_3, ptx_2};110std::vector<CTransactionRef> package_231{ptx_2, ptx_3, ptx_1};111std::vector<CTransactionRef> package_213{ptx_2, ptx_1, ptx_3};112std::vector<CTransactionRef> package_312{ptx_3, ptx_1, ptx_2};113std::vector<CTransactionRef> package_321{ptx_3, ptx_2, ptx_1};115uint256 calculated_hash_123 = (HashWriter() << wtxid_1 << wtxid_2 << wtxid_3).GetSHA256();117uint256 hash_if_by_txid = (HashWriter() << wtxid_2 << wtxid_1 << wtxid_3).GetSHA256();118BOOST_CHECK(hash_if_by_txid != calculated_hash_123);120uint256 hash_if_use_txid = (HashWriter() << txid_2 << txid_1 << txid_3).GetSHA256();121BOOST_CHECK(hash_if_use_txid != calculated_hash_123);123uint256 hash_if_use_int_order = (HashWriter() << wtxid_2 << wtxid_1 << wtxid_3).GetSHA256();124BOOST_CHECK(hash_if_use_int_order != calculated_hash_123);126BOOST_CHECK_EQUAL(calculated_hash_123, GetPackageHash(package_123));127BOOST_CHECK_EQUAL(calculated_hash_123, GetPackageHash(package_132));128BOOST_CHECK_EQUAL(calculated_hash_123, GetPackageHash(package_231));129BOOST_CHECK_EQUAL(calculated_hash_123, GetPackageHash(package_213));130BOOST_CHECK_EQUAL(calculated_hash_123, GetPackageHash(package_312));131BOOST_CHECK_EQUAL(calculated_hash_123, GetPackageHash(package_321));132}134BOOST_AUTO_TEST_CASE(package_sanitization_tests)135{136// Packages can't have more than 25 transactions.137Package package_too_many;138package_too_many.reserve(MAX_PACKAGE_COUNT + 1);139for (size_t i{0}; i < MAX_PACKAGE_COUNT + 1; ++i) {140package_too_many.emplace_back(create_placeholder_tx(1, 1));141}142PackageValidationState state_too_many;143BOOST_CHECK(!IsWellFormedPackage(package_too_many, state_too_many));144BOOST_CHECK_EQUAL(state_too_many.GetResult(), PackageValidationResult::PCKG_POLICY);145BOOST_CHECK_EQUAL(state_too_many.GetRejectReason(), "package-too-many-transactions");147// Packages can't have a total weight of more than 404'000WU.148CTransactionRef large_ptx = create_placeholder_tx(150, 150);149Package package_too_large;150auto size_large = GetTransactionWeight(*large_ptx);151size_t total_weight{0};152while (total_weight <= MAX_PACKAGE_WEIGHT) {153package_too_large.push_back(large_ptx);154total_weight += size_large;155}156BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);157PackageValidationState state_too_large;158BOOST_CHECK(!IsWellFormedPackage(package_too_large, state_too_large));159BOOST_CHECK_EQUAL(state_too_large.GetResult(), PackageValidationResult::PCKG_POLICY);160BOOST_CHECK_EQUAL(state_too_large.GetRejectReason(), "package-too-large");162// Packages can't contain transactions with the same txid.163Package package_duplicate_txids_empty;164for (auto i{0}; i < 3; ++i) {165CMutableTransaction empty_tx;166package_duplicate_txids_empty.emplace_back(MakeTransactionRef(empty_tx));167}168PackageValidationState state_duplicates;169BOOST_CHECK(!IsWellFormedPackage(package_duplicate_txids_empty, state_duplicates));170BOOST_CHECK_EQUAL(state_duplicates.GetResult(), PackageValidationResult::PCKG_POLICY);171BOOST_CHECK_EQUAL(state_duplicates.GetRejectReason(), "package-contains-duplicates");172BOOST_CHECK(!IsConsistentPackage(package_duplicate_txids_empty));174// Packages can't have transactions spending the same prevout175CMutableTransaction tx_zero_1;176CMutableTransaction tx_zero_2;177COutPoint same_prevout{Txid::FromUint256(m_rng.rand256()), 0};178tx_zero_1.vin.emplace_back(same_prevout);179tx_zero_2.vin.emplace_back(same_prevout);180// Different vouts (not the same tx)181tx_zero_1.vout.emplace_back(CENT, P2WSH_OP_TRUE);182tx_zero_2.vout.emplace_back(2 * CENT, P2WSH_OP_TRUE);183Package package_conflicts{MakeTransactionRef(tx_zero_1), MakeTransactionRef(tx_zero_2)};184BOOST_CHECK(!IsConsistentPackage(package_conflicts));185// Transactions are considered sorted when they have no dependencies.186BOOST_CHECK(IsTopoSortedPackage(package_conflicts));187PackageValidationState state_conflicts;188BOOST_CHECK(!IsWellFormedPackage(package_conflicts, state_conflicts));189BOOST_CHECK_EQUAL(state_conflicts.GetResult(), PackageValidationResult::PCKG_POLICY);190BOOST_CHECK_EQUAL(state_conflicts.GetRejectReason(), "conflict-in-package");192// IsConsistentPackage only cares about conflicts between transactions, not about a transaction193// conflicting with itself (i.e. duplicate prevouts in vin).194CMutableTransaction dup_tx;195const COutPoint rand_prevout{Txid::FromUint256(m_rng.rand256()), 0};196dup_tx.vin.emplace_back(rand_prevout);197dup_tx.vin.emplace_back(rand_prevout);198Package package_with_dup_tx{MakeTransactionRef(dup_tx)};199BOOST_CHECK(IsConsistentPackage(package_with_dup_tx));200package_with_dup_tx.emplace_back(create_placeholder_tx(1, 1));201BOOST_CHECK(IsConsistentPackage(package_with_dup_tx));202}204BOOST_AUTO_TEST_CASE(package_validation_tests)205{206LOCK(cs_main);207unsigned int initialPoolSize = m_node.mempool->size();209// Parent and Child Package210CKey parent_key = GenerateRandomKey();211CScript parent_locking_script = GetScriptForDestination(PKHash(parent_key.GetPubKey()));212auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0,213/*input_height=*/0, /*input_signing_key=*/coinbaseKey,214/*output_destination=*/parent_locking_script,215/*output_amount=*/CAmount(49 * COIN), /*submit=*/false);216CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);218CKey child_key = GenerateRandomKey();219CScript child_locking_script = GetScriptForDestination(PKHash(child_key.GetPubKey()));220auto mtx_child = CreateValidMempoolTransaction(/*input_transaction=*/tx_parent, /*input_vout=*/0,221/*input_height=*/101, /*input_signing_key=*/parent_key,222/*output_destination=*/child_locking_script,223/*output_amount=*/CAmount(48 * COIN), /*submit=*/false);224CTransactionRef tx_child = MakeTransactionRef(mtx_child);225Package package_parent_child{tx_parent, tx_child};226const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_parent_child, /*test_accept=*/true, /*client_maxfeerate=*/{});227if (auto err_parent_child{CheckPackageMempoolAcceptResult(package_parent_child, result_parent_child, /*expect_valid=*/true, nullptr)}) {228BOOST_ERROR(err_parent_child.value());229} else {230auto it_parent = result_parent_child.m_tx_results.find(tx_parent->GetWitnessHash());231auto it_child = result_parent_child.m_tx_results.find(tx_child->GetWitnessHash());233BOOST_CHECK(it_parent->second.m_effective_feerate.value().GetFee(GetVirtualTransactionSize(*tx_parent)) == COIN);234BOOST_CHECK_EQUAL(it_parent->second.m_wtxids_fee_calculations.value().size(), 1);235BOOST_CHECK_EQUAL(it_parent->second.m_wtxids_fee_calculations.value().front(), tx_parent->GetWitnessHash());237BOOST_CHECK(it_child->second.m_effective_feerate.value().GetFee(GetVirtualTransactionSize(*tx_child)) == COIN);238BOOST_CHECK_EQUAL(it_child->second.m_wtxids_fee_calculations.value().size(), 1);239BOOST_CHECK_EQUAL(it_child->second.m_wtxids_fee_calculations.value().front(), tx_child->GetWitnessHash());240}241// A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.242CTransactionRef giant_ptx = create_placeholder_tx(999, 999);243BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > DEFAULT_CLUSTER_SIZE_LIMIT_KVB * 1000);244Package package_single_giant{giant_ptx};245auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_single_giant, /*test_accept=*/true, /*client_maxfeerate=*/{});246if (auto err_single_large{CheckPackageMempoolAcceptResult(package_single_giant, result_single_large, /*expect_valid=*/false, nullptr)}) {247BOOST_ERROR(err_single_large.value());248} else {249BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);250BOOST_CHECK_EQUAL(result_single_large.m_state.GetRejectReason(), "transaction failed");
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.