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.
1001// This means a child with its fee delta from prioritisetransaction can pay for a parent.1002m_node.mempool->PrioritiseTransaction(tx_child_cheap->GetHash(), 1 * COIN);1003// Now that the child's fees have "increased" by 1 BTC, the cheap package should succeed.1004{1005const auto submit_prioritised_package = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,1006package_still_too_low, /*test_accept=*/false, /*client_maxfeerate=*/{});1007if (auto err_prioritised{CheckPackageMempoolAcceptResult(package_still_too_low, submit_prioritised_package, /*expect_valid=*/true, m_node.mempool.get())}) {1008BOOST_ERROR(err_prioritised.value());1009} else {1010const CFeeRate expected_feerate(1 * COIN + parent_fee + child_fee,1011GetVirtualTransactionSize(*tx_parent_cheap) + GetVirtualTransactionSize(*tx_child_cheap));1012BOOST_CHECK_EQUAL(submit_prioritised_package.m_tx_results.size(), package_still_too_low.size());1013auto it_parent = submit_prioritised_package.m_tx_results.find(tx_parent_cheap->GetWitnessHash());1014auto it_child = submit_prioritised_package.m_tx_results.find(tx_child_cheap->GetWitnessHash());1015BOOST_CHECK(it_parent->second.m_result_type == MempoolAcceptResult::ResultType::VALID);1016BOOST_CHECK(it_parent->second.m_base_fees.value() == parent_fee);1017BOOST_CHECK(it_parent->second.m_effective_feerate.value() == expected_feerate);1018BOOST_CHECK(it_child->second.m_result_type == MempoolAcceptResult::ResultType::VALID);1019BOOST_CHECK(it_child->second.m_base_fees.value() == child_fee);1020BOOST_CHECK(it_child->second.m_effective_feerate.value() == expected_feerate);1021std::vector<Wtxid> expected_wtxids({tx_parent_cheap->GetWitnessHash(), tx_child_cheap->GetWitnessHash()});1022BOOST_CHECK(it_parent->second.m_wtxids_fee_calculations.value() == expected_wtxids);1023BOOST_CHECK(it_child->second.m_wtxids_fee_calculations.value() == expected_wtxids);1024}1025expected_pool_size += 2;1026BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);1027}1029// Package feerate is calculated without topology in mind; it's just aggregating fees and sizes.1030// However, this should not allow parents to pay for children. Each transaction should be1031// validated individually first, eliminating sufficient-feerate parents before they are unfairly1032// included in the package feerate. It's also important that the low-fee child doesn't prevent1033// the parent from being accepted.1034Package package_rich_parent;1035const CAmount high_parent_fee{1 * COIN};1036auto mtx_parent_rich = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[2], /*input_vout=*/0,1037/*input_height=*/0, /*input_signing_key=*/coinbaseKey,1038/*output_destination=*/parent_spk,1039/*output_amount=*/coinbase_value - high_parent_fee, /*submit=*/false);1040CTransactionRef tx_parent_rich = MakeTransactionRef(mtx_parent_rich);1041package_rich_parent.push_back(tx_parent_rich);1043auto mtx_child_poor = CreateValidMempoolTransaction(/*input_transaction=*/tx_parent_rich, /*input_vout=*/0,1044/*input_height=*/101, /*input_signing_key=*/child_key,1045/*output_destination=*/child_spk,1046/*output_amount=*/coinbase_value - high_parent_fee, /*submit=*/false);1047CTransactionRef tx_child_poor = MakeTransactionRef(mtx_child_poor);1048package_rich_parent.push_back(tx_child_poor);1050// Parent pays 1 BTC and child pays none. The parent should be accepted without the child.1051{1052BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);1053const auto submit_rich_parent = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool,1054package_rich_parent, /*test_accept=*/false, /*client_maxfeerate=*/{});1055if (auto err_rich_parent{CheckPackageMempoolAcceptResult(package_rich_parent, submit_rich_parent, /*expect_valid=*/false, m_node.mempool.get())}) {1056BOOST_ERROR(err_rich_parent.value());1057} else {1058// The child would have been validated on its own and failed.1059BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetResult(), PackageValidationResult::PCKG_TX);1060BOOST_CHECK_EQUAL(submit_rich_parent.m_state.GetRejectReason(), "transaction failed");1062auto it_parent = submit_rich_parent.m_tx_results.find(tx_parent_rich->GetWitnessHash());1063auto it_child = submit_rich_parent.m_tx_results.find(tx_child_poor->GetWitnessHash());1064BOOST_CHECK(it_parent->second.m_result_type == MempoolAcceptResult::ResultType::VALID);1065BOOST_CHECK(it_child->second.m_result_type == MempoolAcceptResult::ResultType::INVALID);1066BOOST_CHECK(it_parent->second.m_state.GetRejectReason() == "");1067BOOST_CHECK_MESSAGE(it_parent->second.m_base_fees.value() == high_parent_fee,1068strprintf("rich parent: expected fee %s, got %s", high_parent_fee, it_parent->second.m_base_fees.value()));1069BOOST_CHECK(it_parent->second.m_effective_feerate == CFeeRate(high_parent_fee, GetVirtualTransactionSize(*tx_parent_rich)));1070BOOST_CHECK_EQUAL(it_child->second.m_result_type, MempoolAcceptResult::ResultType::INVALID);1071BOOST_CHECK_EQUAL(it_child->second.m_state.GetResult(), TxValidationResult::TX_RECONSIDERABLE);1072BOOST_CHECK_EQUAL(it_child->second.m_state.GetRejectReason(), "mempool min fee not met");1073}1074expected_pool_size += 1;1075BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);1076}1077}1079BOOST_AUTO_TEST_CASE(package_rbf_tests)1080{1081mineBlocks(5);1082LOCK(::cs_main);1083size_t expected_pool_size = m_node.mempool->size();1084CKey child_key{GenerateRandomKey()};1085CScript parent_spk = GetScriptForDestination(WitnessV0KeyHash(child_key.GetPubKey()));1086CKey grandchild_key{GenerateRandomKey()};1087CScript child_spk = GetScriptForDestination(WitnessV0KeyHash(grandchild_key.GetPubKey()));1089const CAmount coinbase_value{50 * COIN};1090// Test that de-duplication works. This is not actually package rbf.1091{1092// 1 parent paying 200sat, 1 child paying 300sat1093Package package1;1094// 1 parent paying 200sat, 1 child paying 500sat1095Package package2;1096// Package1 and package2 have the same parent. The children conflict.1097auto mtx_parent = CreateValidMempoolTransaction(/*input_transaction=*/m_coinbase_txns[0], /*input_vout=*/0,1098/*input_height=*/0, /*input_signing_key=*/coinbaseKey,1099/*output_destination=*/parent_spk,1100/*output_amount=*/coinbase_value - low_fee_amt, /*submit=*/false);1101CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);1102package1.push_back(tx_parent);1103package2.push_back(tx_parent);1105CTransactionRef tx_child_1 = MakeTransactionRef(CreateValidMempoolTransaction(tx_parent, 0, 101, child_key, child_spk, coinbase_value - low_fee_amt - 300, false));1106package1.push_back(tx_child_1);1107CTransactionRef tx_child_2 = MakeTransactionRef(CreateValidMempoolTransaction(tx_parent, 0, 101, child_key, child_spk, coinbase_value - low_fee_amt - 500, false));1108package2.push_back(tx_child_2);1110LOCK(m_node.mempool->cs);1111const auto submit1 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package1, /*test_accept=*/false, std::nullopt);1112if (auto err_1{CheckPackageMempoolAcceptResult(package1, submit1, /*expect_valid=*/true, m_node.mempool.get())}) {1113BOOST_ERROR(err_1.value());1114}1116// Check precise ResultTypes and mempool size. We know it_parent_1 and it_child_1 exist from above call1117auto it_parent_1 = submit1.m_tx_results.find(tx_parent->GetWitnessHash());1118auto it_child_1 = submit1.m_tx_results.find(tx_child_1->GetWitnessHash());1119BOOST_CHECK_EQUAL(it_parent_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1120BOOST_CHECK_EQUAL(it_child_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1121expected_pool_size += 2;1122BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);1124const auto submit2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package2, /*test_accept=*/false, std::nullopt);1125if (auto err_2{CheckPackageMempoolAcceptResult(package2, submit2, /*expect_valid=*/true, m_node.mempool.get())}) {1126BOOST_ERROR(err_2.value());1127}1129// Check precise ResultTypes and mempool size. We know it_parent_2 and it_child_2 exist from above call1130auto it_parent_2 = submit2.m_tx_results.find(tx_parent->GetWitnessHash());1131auto it_child_2 = submit2.m_tx_results.find(tx_child_2->GetWitnessHash());1132BOOST_CHECK_EQUAL(it_parent_2->second.m_result_type, MempoolAcceptResult::ResultType::MEMPOOL_ENTRY);1133BOOST_CHECK_EQUAL(it_child_2->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1134BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);1136// child1 has been replaced1137BOOST_CHECK(!m_node.mempool->exists(tx_child_1->GetHash()));1138}1140// Test package rbf.1141{1142CTransactionRef tx_parent_1 = MakeTransactionRef(CreateValidMempoolTransaction(1143m_coinbase_txns[1], /*input_vout=*/0, /*input_height=*/0,1144coinbaseKey, parent_spk, coinbase_value - 200, /*submit=*/false));1145CTransactionRef tx_child_1 = MakeTransactionRef(CreateValidMempoolTransaction(1146tx_parent_1, /*input_vout=*/0, /*input_height=*/101,1147child_key, child_spk, coinbase_value - 400, /*submit=*/false));1149CTransactionRef tx_parent_2 = MakeTransactionRef(CreateValidMempoolTransaction(1150m_coinbase_txns[1], /*input_vout=*/0, /*input_height=*/0,1151coinbaseKey, parent_spk, coinbase_value - 800, /*submit=*/false));1152CTransactionRef tx_child_2 = MakeTransactionRef(CreateValidMempoolTransaction(1153tx_parent_2, /*input_vout=*/0, /*input_height=*/101,1154child_key, child_spk, coinbase_value - 800 - 200, /*submit=*/false));1156CTransactionRef tx_parent_3 = MakeTransactionRef(CreateValidMempoolTransaction(1157m_coinbase_txns[1], /*input_vout=*/0, /*input_height=*/0,1158coinbaseKey, parent_spk, coinbase_value - 199, /*submit=*/false));1159CTransactionRef tx_child_3 = MakeTransactionRef(CreateValidMempoolTransaction(1160tx_parent_3, /*input_vout=*/0, /*input_height=*/101,1161child_key, child_spk, coinbase_value - 199 - 1300, /*submit=*/false));1163// In all packages, the parents conflict with each other1164BOOST_CHECK(tx_parent_1->GetHash() != tx_parent_2->GetHash());1165BOOST_CHECK(tx_parent_2->GetHash() != tx_parent_3->GetHash());1167// 1 parent paying 200sat, 1 child paying 200sat.1168Package package1{tx_parent_1, tx_child_1};1169// 1 parent paying 800sat, 1 child paying 200sat.1170Package package2{tx_parent_2, tx_child_2};1171// 1 parent paying 199sat, 1 child paying 1300sat.1172Package package3{tx_parent_3, tx_child_3};1174const auto submit1 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package1, false, std::nullopt);1175if (auto err_1{CheckPackageMempoolAcceptResult(package1, submit1, /*expect_valid=*/true, m_node.mempool.get())}) {1176BOOST_ERROR(err_1.value());1177}1178auto it_parent_1 = submit1.m_tx_results.find(tx_parent_1->GetWitnessHash());1179auto it_child_1 = submit1.m_tx_results.find(tx_child_1->GetWitnessHash());1180BOOST_CHECK_EQUAL(it_parent_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1181BOOST_CHECK_EQUAL(it_child_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1182expected_pool_size += 2;1183BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);1185// This replacement is actually not package rbf; the parent carries enough fees1186// to replace the entire package on its own.1187const auto submit2 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package2, false, std::nullopt);1188if (auto err_2{CheckPackageMempoolAcceptResult(package2, submit2, /*expect_valid=*/true, m_node.mempool.get())}) {1189BOOST_ERROR(err_2.value());1190}1191auto it_parent_2 = submit2.m_tx_results.find(tx_parent_2->GetWitnessHash());1192auto it_child_2 = submit2.m_tx_results.find(tx_child_2->GetWitnessHash());1193BOOST_CHECK_EQUAL(it_parent_2->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1194BOOST_CHECK_EQUAL(it_child_2->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1195BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);1197// Package RBF, in which the replacement transaction's child sponsors the fees to meet RBF feerate rules1198const auto submit3 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package3, false, std::nullopt);1199if (auto err_3{CheckPackageMempoolAcceptResult(package3, submit3, /*expect_valid=*/true, m_node.mempool.get())}) {1200BOOST_ERROR(err_3.value());1201}1202auto it_parent_3 = submit3.m_tx_results.find(tx_parent_3->GetWitnessHash());1203auto it_child_3 = submit3.m_tx_results.find(tx_child_3->GetWitnessHash());1204BOOST_CHECK_EQUAL(it_parent_3->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1205BOOST_CHECK_EQUAL(it_child_3->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1207// package3 was considered as a package to replace both package2 transactions1208BOOST_CHECK(it_parent_3->second.m_replaced_transactions.size() == 2);1209BOOST_CHECK(it_child_3->second.m_replaced_transactions.empty());1211std::vector<Wtxid> expected_package3_wtxids({tx_parent_3->GetWitnessHash(), tx_child_3->GetWitnessHash()});1212const auto package3_total_vsize{GetVirtualTransactionSize(*tx_parent_3) + GetVirtualTransactionSize(*tx_child_3)};1213BOOST_CHECK(it_parent_3->second.m_wtxids_fee_calculations.value() == expected_package3_wtxids);1214BOOST_CHECK(it_child_3->second.m_wtxids_fee_calculations.value() == expected_package3_wtxids);1215BOOST_CHECK_EQUAL(it_parent_3->second.m_effective_feerate.value().GetFee(package3_total_vsize), 199 + 1300);1216BOOST_CHECK_EQUAL(it_child_3->second.m_effective_feerate.value().GetFee(package3_total_vsize), 199 + 1300);1218BOOST_CHECK_EQUAL(m_node.mempool->size(), expected_pool_size);1220// Finally, check that we can prioritise tx_child_1 to get package1 into the mempool.1221// It should not be possible to resubmit package1 and get it in without prioritisation.1222const auto submit4 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package1, false, std::nullopt);1223if (auto err_4{CheckPackageMempoolAcceptResult(package1, submit4, /*expect_valid=*/false, m_node.mempool.get())}) {1224BOOST_ERROR(err_4.value());1225}1226m_node.mempool->PrioritiseTransaction(tx_child_1->GetHash(), 1363);1227const auto submit5 = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package1, false, std::nullopt);1228if (auto err_5{CheckPackageMempoolAcceptResult(package1, submit5, /*expect_valid=*/true, m_node.mempool.get())}) {1229BOOST_ERROR(err_5.value());1230}1231it_parent_1 = submit5.m_tx_results.find(tx_parent_1->GetWitnessHash());1232it_child_1 = submit5.m_tx_results.find(tx_child_1->GetWitnessHash());1233BOOST_CHECK_EQUAL(it_parent_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1234BOOST_CHECK_EQUAL(it_child_1->second.m_result_type, MempoolAcceptResult::ResultType::VALID);1235LOCK(m_node.mempool->cs);1236BOOST_CHECK(m_node.mempool->GetIter(tx_parent_1->GetHash()).has_value());1237BOOST_CHECK(m_node.mempool->GetIter(tx_child_1->GetHash()).has_value());1238}1239}1240BOOST_AUTO_TEST_SUITE_END()
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.