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.
252// Pick highest ancestor feerate entry.253auto best_iter = m_entries.begin();254Assume(best_iter != m_entries.end());255const auto ancestor_package_size = (*best_iter)->second.GetSizeWithAncestors();256const auto ancestor_package_fee = (*best_iter)->second.GetModFeesWithAncestors();257// Stop here. Everything that didn't "make it into the block" has bumpfee.258if (target_feerate.has_value() &&259ancestor_package_fee < target_feerate->GetFee(ancestor_package_size)) {260break;261}263// Calculate ancestors on the fly. This lookup should be fairly cheap, and ancestor sets264// change at every iteration, so this is more efficient than maintaining a cache.265std::set<MockEntryMap::iterator, IteratorComparator> ancestors;266{267std::set<MockEntryMap::iterator, IteratorComparator> to_process;268to_process.insert(*best_iter);269while (!to_process.empty()) {270auto iter = to_process.begin();271Assume(iter != to_process.end());272ancestors.insert(*iter);273for (const auto& input : (*iter)->second.GetTx().vin) {274if (auto parent_it{m_entries_by_txid.find(input.prevout.hash)}; parent_it != m_entries_by_txid.end()) {275if (!ancestors.contains(parent_it)) {276to_process.insert(parent_it);277}278}279}280to_process.erase(iter);281}282}283// Track the order in which transactions were selected.284for (const auto& ancestor : ancestors) {285m_inclusion_order.emplace(ancestor->first, sequence_num);286}287DeleteAncestorPackage(ancestors);288SanityCheck();289++sequence_num;290}291if (!target_feerate.has_value()) {292Assume(m_in_block.size() == num_txns);293} else {294Assume(m_in_block.empty() || m_total_fees >= target_feerate->GetFee(m_total_vsize));295}296Assume(m_in_block.empty() || sequence_num > 0);297Assume(m_in_block.size() == m_inclusion_order.size());298// Do not try to continue building the block template with a different feerate.299m_ready_to_calculate = false;300}303std::map<Txid, uint32_t> MiniMiner::Linearize()304{305BuildMockTemplate(std::nullopt);306return m_inclusion_order;307}309std::map<COutPoint, CAmount> MiniMiner::CalculateBumpFees(const CFeeRate& target_feerate)310{311if (!m_ready_to_calculate) return {};312// Build a block template until the target feerate is hit.313BuildMockTemplate(target_feerate);315// Each transaction that "made it into the block" has a bumpfee of 0, i.e. they are part of an316// ancestor package with at least the target feerate and don't need to be bumped.317for (const auto& txid : m_in_block) {318// Not all of the block transactions were necessarily requested.319auto it = m_requested_outpoints_by_txid.find(txid);320if (it != m_requested_outpoints_by_txid.end()) {321for (const auto& outpoint : it->second) {322m_bump_fees.emplace(outpoint, 0);323}324m_requested_outpoints_by_txid.erase(it);325}326}328// A transactions and its ancestors will only be picked into a block when329// both the ancestor set feerate and the individual feerate meet the target330// feerate.331//332// We had to convince ourselves that after running the mini miner and333// picking all eligible transactions into our MockBlockTemplate, there334// could still be transactions remaining that have a lower individual335// feerate than their ancestor feerate. So here is an example:336//337// ┌─────────────────┐338// │ │339// │ Grandparent │340// │ 1700 vB │341// │ 1700 sats │ Target feerate: 10 s/vB342// │ 1 s/vB │ GP Ancestor Set Feerate (ASFR): 1 s/vB343// │ │ P1_ASFR: 9.84 s/vB344// └──────▲───▲──────┘ P2_ASFR: 2.47 s/vB345// │ │ C_ASFR: 10.27 s/vB346// ┌───────────────┐ │ │ ┌──────────────┐347// │ ├────┘ └────┤ │ ⇒ C_FR < TFR < C_ASFR348// │ Parent 1 │ │ Parent 2 │349// │ 200 vB │ │ 200 vB │350// │ 17000 sats │ │ 3000 sats │351// │ 85 s/vB │ │ 15 s/vB │352// │ │ │ │353// └───────────▲───┘ └───▲──────────┘354// │ │355// │ ┌───────────┐ │356// └────┤ ├────┘357// │ Child │358// │ 100 vB │359// │ 900 sats │360// │ 9 s/vB │361// │ │362// └───────────┘363//364// We therefore calculate both the bump fee that is necessary to elevate365// the individual transaction to the target feerate:366// target_feerate × tx_size - tx_fees367// and the bump fee that is necessary to bump the entire ancestor set to368// the target feerate:369// target_feerate × ancestor_set_size - ancestor_set_fees370// By picking the maximum from the two, we ensure that a transaction meets371// both criteria.372for (const auto& [txid, outpoints] : m_requested_outpoints_by_txid) {373auto it = m_entries_by_txid.find(txid);374Assume(it != m_entries_by_txid.end());375if (it != m_entries_by_txid.end()) {376Assume(target_feerate.GetFee(it->second.GetSizeWithAncestors()) > std::min(it->second.GetModifiedFee(), it->second.GetModFeesWithAncestors()));377CAmount bump_fee_with_ancestors = target_feerate.GetFee(it->second.GetSizeWithAncestors()) - it->second.GetModFeesWithAncestors();378CAmount bump_fee_individual = target_feerate.GetFee(it->second.GetTxSize()) - it->second.GetModifiedFee();379const CAmount bump_fee{std::max(bump_fee_with_ancestors, bump_fee_individual)};380Assume(bump_fee >= 0);381for (const auto& outpoint : outpoints) {382m_bump_fees.emplace(outpoint, bump_fee);383}384}385}386return m_bump_fees;387}389std::optional<CAmount> MiniMiner::CalculateTotalBumpFees(const CFeeRate& target_feerate)390{391if (!m_ready_to_calculate) return std::nullopt;392// Build a block template until the target feerate is hit.393BuildMockTemplate(target_feerate);395// All remaining ancestors that are not part of m_in_block must be bumped, but no other relatives396std::set<MockEntryMap::iterator, IteratorComparator> ancestors;397std::set<MockEntryMap::iterator, IteratorComparator> to_process;398for (const auto& [txid, outpoints] : m_requested_outpoints_by_txid) {399// Skip any ancestors that already have a miner score higher than the target feerate400// (already "made it" into the block)401if (m_in_block.contains(txid)) continue;402auto iter = m_entries_by_txid.find(txid);403if (iter == m_entries_by_txid.end()) continue;404to_process.insert(iter);405ancestors.insert(iter);406}408std::set<Txid> has_been_processed;409while (!to_process.empty()) {410auto iter = to_process.begin();411const CTransaction& tx = (*iter)->second.GetTx();412for (const auto& input : tx.vin) {413if (auto parent_it{m_entries_by_txid.find(input.prevout.hash)}; parent_it != m_entries_by_txid.end()) {414if (!has_been_processed.contains(input.prevout.hash)) {415to_process.insert(parent_it);416}417ancestors.insert(parent_it);418}419}420has_been_processed.insert(tx.GetHash());421to_process.erase(iter);422}423const auto ancestor_package_size = std::accumulate(ancestors.cbegin(), ancestors.cend(), int64_t{0},424[](int64_t sum, const auto it) {return sum + it->second.GetTxSize();});425const auto ancestor_package_fee = std::accumulate(ancestors.cbegin(), ancestors.cend(), CAmount{0},426[](CAmount sum, const auto it) {return sum + it->second.GetModifiedFee();});427return target_feerate.GetFee(ancestor_package_size) - ancestor_package_fee;428}429} // namespace node
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.