Inside Bitcoin
Opening the selected file…
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.
1001m_subpackage.m_conflicting_fees += it->GetModifiedFee();1002m_subpackage.m_conflicting_size += it->GetTxSize();1003}1005if (const auto err_string{PaysForRBF(m_subpackage.m_conflicting_fees, ws.m_modified_fees, ws.m_vsize,1006m_pool.m_opts.incremental_relay_feerate, hash)}) {1007// Result may change in a package context1008return state.Invalid(TxValidationResult::TX_RECONSIDERABLE,1009strprintf("insufficient fee%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);1010}1012// Add all the to-be-removed transactions to the changeset.1013for (auto it : all_conflicts) {1014m_subpackage.m_changeset->StageRemoval(it);1015}1017// Run cluster size limit checks and fail if we exceed them.1018if (!m_subpackage.m_changeset->CheckMemPoolPolicyLimits()) {1019return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-large-cluster", "");1020}1022if (const auto err_string{ImprovesFeerateDiagram(*m_subpackage.m_changeset)}) {1023// We checked above for the cluster size limits being respected, so a1024// failure here can only be due to an insufficient fee.1025Assume(err_string->first == DiagramCheckError::FAILURE);1026return state.Invalid(TxValidationResult::TX_RECONSIDERABLE, "replacement-failed", err_string->second);1027}1029return true;1030}1032bool MemPoolAccept::PackageRBFChecks(const std::vector<CTransactionRef>& txns,1033std::vector<Workspace>& workspaces,1034PackageValidationState& package_state)1035{1036AssertLockHeld(cs_main);1037AssertLockHeld(m_pool.cs);1039assert(std::all_of(txns.cbegin(), txns.cend(), [this](const auto& tx)1040{ return !m_pool.exists(tx->GetHash());}));1042assert(txns.size() == workspaces.size());1044// We're in package RBF context; replacement proposal must be size 21045if (workspaces.size() != 2 || !Assume(IsChildWithParents(txns))) {1046return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package RBF failed: package must be 1-parent-1-child");1047}1049// If the package has in-mempool parents, we won't consider a package RBF1050// since it would result in a cluster larger than 2.1051// N.B. To relax this constraint we will need to revisit how CCoinsViewMemPool::PackageAddTransaction1052// is being used inside AcceptMultipleTransactions to track available inputs while processing a package.1053// Specifically we would need to check that the ancestors of the new1054// transactions don't intersect with the set of transactions to be removed1055// due to RBF, which is not checked at all in the package acceptance1056// context.1057for (const auto& ws : workspaces) {1058if (!ws.m_parents.empty()) {1059return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package RBF failed: new transaction cannot have mempool ancestors");1060}1061}1063// Aggregate all conflicts into one set.1064CTxMemPool::setEntries direct_conflict_iters;1065for (Workspace& ws : workspaces) {1066// Aggregate all conflicts into one set.1067direct_conflict_iters.merge(ws.m_iters_conflicting);1068}1070const auto& parent_ws = workspaces[0];1071const auto& child_ws = workspaces[1];1073// Don't consider replacements that would cause us to remove a large number of mempool entries.1074// This limit is not increased in a package RBF. Use the aggregate number of transactions.1075CTxMemPool::setEntries all_conflicts;1076if (const auto err_string{GetEntriesForConflicts(*child_ws.m_ptx, m_pool, direct_conflict_iters,1077all_conflicts)}) {1078return package_state.Invalid(PackageValidationResult::PCKG_POLICY,1079"package RBF failed: too many potential replacements", *err_string);1080}1082for (CTxMemPool::txiter it : all_conflicts) {1083m_subpackage.m_changeset->StageRemoval(it);1084m_subpackage.m_conflicting_fees += it->GetModifiedFee();1085m_subpackage.m_conflicting_size += it->GetTxSize();1086}1088// Use the child as the transaction for attributing errors to.1089const Txid& child_hash = child_ws.m_ptx->GetHash();1090if (const auto err_string{PaysForRBF(/*original_fees=*/m_subpackage.m_conflicting_fees,1091/*replacement_fees=*/m_subpackage.m_total_modified_fees,1092/*replacement_vsize=*/m_subpackage.m_total_vsize,1093m_pool.m_opts.incremental_relay_feerate, child_hash)}) {1094return package_state.Invalid(PackageValidationResult::PCKG_POLICY,1095"package RBF failed: insufficient anti-DoS fees", *err_string);1096}1098// Ensure this two transaction package is a "chunk" on its own; we don't want the child1099// to be only paying anti-DoS fees1100const CFeeRate parent_feerate(parent_ws.m_modified_fees, parent_ws.m_vsize);1101const CFeeRate package_feerate(m_subpackage.m_total_modified_fees, m_subpackage.m_total_vsize);1102if (package_feerate <= parent_feerate) {1103return package_state.Invalid(PackageValidationResult::PCKG_POLICY,1104"package RBF failed: package feerate is less than or equal to parent feerate",1105strprintf("package feerate %s <= parent feerate is %s", package_feerate.ToString(), parent_feerate.ToString()));1106}1108// Run cluster size limit checks and fail if we exceed them.1109if (!m_subpackage.m_changeset->CheckMemPoolPolicyLimits()) {1110return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "too-large-cluster", "");1111}1113// Check if it's economically rational to mine this package rather than the ones it replaces.1114if (const auto err_tup{ImprovesFeerateDiagram(*m_subpackage.m_changeset)}) {1115Assume(err_tup->first == DiagramCheckError::FAILURE);1116return package_state.Invalid(PackageValidationResult::PCKG_POLICY,1117"package RBF failed: " + err_tup.value().second, "");1118}1120LogDebug(BCLog::TXPACKAGES, "package RBF checks passed: parent %s (wtxid=%s), child %s (wtxid=%s), package hash (%s)\n",1121txns.front()->GetHash().ToString(), txns.front()->GetWitnessHash().ToString(),1122txns.back()->GetHash().ToString(), txns.back()->GetWitnessHash().ToString(),1123GetPackageHash(txns).ToString());1126return true;1127}1129bool MemPoolAccept::PolicyScriptChecks(Workspace& ws)1130{1131AssertLockHeld(cs_main);1132AssertLockHeld(m_pool.cs);1133const CTransaction& tx = *ws.m_ptx;1134TxValidationState& state = ws.m_state;1136constexpr script_verify_flags scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;1138// Check input scripts and signatures.1139// This is done last to help prevent CPU exhaustion denial-of-service attacks.1140if (!CheckInputScripts(tx, state, m_view, scriptVerifyFlags, true, false, ws.m_precomputed_txdata, GetValidationCache())) {1141// Detect a failure due to a missing witness so that p2p code can handle rejection caching appropriately.1142if (!tx.HasWitness() && SpendsNonAnchorWitnessProg(tx, m_view)) {1143state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,1144state.GetRejectReason(), state.GetDebugMessage());1145}1146return false; // state filled in by CheckInputScripts1147}1149return true;1150}1152bool MemPoolAccept::ConsensusScriptChecks(Workspace& ws)1153{1154AssertLockHeld(cs_main);1155AssertLockHeld(m_pool.cs);1156const CTransaction& tx = *ws.m_ptx;1157const Txid& hash = ws.m_hash;1158TxValidationState& state = ws.m_state;1160// Check again against the current block tip's script verification1161// flags to cache our script execution flags. This is, of course,1162// useless if the next block has different script flags from the1163// previous one, but because the cache tracks script flags for us it1164// will auto-invalidate and we'll just have a few blocks of extra1165// misses on soft-fork activation.1166//1167// This is also useful in case of bugs in the standard flags that cause1168// transactions to pass as valid when they're actually invalid. For1169// instance the STRICTENC flag was incorrectly allowing certain1170// CHECKSIG NOT scripts to pass, even though they were invalid.1171//1172// There is a similar check in CreateNewBlock() to prevent creating1173// invalid blocks (using TestBlockValidity), however allowing such1174// transactions into the mempool can be exploited as a DoS attack.1175script_verify_flags currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};1176if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,1177ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {1178LogError("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s", hash.ToString(), state.ToString());1179return Assume(false);1180}1182return true;1183}1185void MemPoolAccept::FinalizeSubpackage(const ATMPArgs& args)1186{1187AssertLockHeld(cs_main);1188AssertLockHeld(m_pool.cs);1190if (!m_subpackage.m_changeset->GetRemovals().empty()) Assume(args.m_allow_replacement);1191// Remove conflicting transactions from the mempool1192for (CTxMemPool::txiter it : m_subpackage.m_changeset->GetRemovals())1193{1194std::string log_string = strprintf("replacing mempool tx %s (wtxid=%s, fees=%s, vsize=%s). ",1195it->GetTx().GetHash().ToString(),1196it->GetTx().GetWitnessHash().ToString(),1197it->GetFee(),1198it->GetTxSize());1199FeeFrac feerate{m_subpackage.m_total_modified_fees, int32_t(m_subpackage.m_total_vsize)};1200uint256 tx_or_package_hash{};1201const bool replaced_with_tx{m_subpackage.m_changeset->GetTxCount() == 1};1202if (replaced_with_tx) {1203const CTransaction& tx = m_subpackage.m_changeset->GetAddedTxn(0);1204tx_or_package_hash = tx.GetHash().ToUint256();1205log_string += strprintf("New tx %s (wtxid=%s, fees=%s, vsize=%s)",1206tx.GetHash().ToString(),1207tx.GetWitnessHash().ToString(),1208feerate.fee,1209feerate.size);1210} else {1211tx_or_package_hash = GetPackageHash(m_subpackage.m_changeset->GetAddedTxns());1212log_string += strprintf("New package %s with %lu txs, fees=%s, vsize=%s",1213tx_or_package_hash.ToString(),1214m_subpackage.m_changeset->GetTxCount(),1215feerate.fee,1216feerate.size);1218}1219LogDebug(BCLog::MEMPOOL, "%s\n", log_string);1220TRACEPOINT(mempool, replaced,1221it->GetTx().GetHash().data(),1222it->GetTxSize(),1223it->GetFee(),1224std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(it->GetTime()).count(),1225tx_or_package_hash.data(),1226feerate.size,1227feerate.fee,1228replaced_with_tx1229);1230m_subpackage.m_replaced_transactions.push_back(it->GetSharedTx());1231}1232m_subpackage.m_changeset->Apply();1233m_subpackage.m_changeset.reset();1234}1236bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>& workspaces,1237PackageValidationState& package_state,1238std::map<Wtxid, MempoolAcceptResult>& results)1239{1240AssertLockHeld(cs_main);1241AssertLockHeld(m_pool.cs);1242// Sanity check: none of the transactions should be in the mempool, and none of the transactions1243// should have a same-txid-different-witness equivalent in the mempool.1244assert(std::all_of(workspaces.cbegin(), workspaces.cend(), [this](const auto& ws) { return !m_pool.exists(ws.m_ptx->GetHash()); }));1246bool all_submitted = true;1247FinalizeSubpackage(args);1248// ConsensusScriptChecks adds to the script cache and is therefore consensus-critical;1249// CheckInputsFromMempoolAndCache asserts that transactions only spend coins available from the1250// mempool or UTXO set. Submit each transaction to the mempool immediately after calling