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) 2011-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 <policy/fees/block_policy_estimator.h>6#include <policy/fees/estimator_args.h>7#include <policy/policy.h>8#include <test/util/setup_common.h>9#include <test/util/txmempool.h>10#include <txmempool.h>11#include <uint256.h>12#include <util/time.h>13#include <validationinterface.h>15#include <boost/test/unit_test.hpp>17BOOST_FIXTURE_TEST_SUITE(blockpolicyestimator_tests, ChainTestingSetup)19BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)20{21CBlockPolicyEstimator feeEst{BlockPolicyFeeEstPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES};22TestMemPoolEntryHelper entry;23CAmount basefee(2000);24CAmount deltaFee(100);25std::vector<CAmount> feeV;26feeV.reserve(10);28// Populate vectors of increasing fees29for (int j = 0; j < 10; j++) {30feeV.push_back(basefee * (j+1));31}33// Store the hashes of transactions that have been34// added to the mempool by their associate fee35// mempool_txs[j] is populated with transactions either of36// fee = basefee * (j+1)37std::list<CTxMemPoolEntry> mempool_txs[10];39// Create a transaction template40CScript garbage;41for (unsigned int i = 0; i < 128; i++)42garbage.push_back('X');43CMutableTransaction tx;44tx.vin.resize(1);45tx.vin[0].scriptSig = garbage;46tx.vout.resize(1);47tx.vout[0].nValue=0LL;48CFeeRate baseRate(basefee, GetVirtualTransactionSize(CTransaction(tx)));50// Create a fake block51std::vector<RemovedMempoolTransactionInfo> block_txs;52int blocknum = 0;54// Loop through 200 blocks55// At a decay .9952 and 4 fee transactions per block56// This makes the tx count about 2.5 per bucket, well above the 0.1 threshold57while (blocknum < 200) {58for (int j = 0; j < 10; j++) { // For each fee59for (int k = 0; k < 4; k++) { // add 4 fee txs60tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique61// Simulate the tx being added to the mempool by calling processTransaction(tx_info)62mempool_txs[j].emplace_back(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));63const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));64const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),65feeV[j],66virtual_size,67entry.nHeight,68/*mempool_limit_bypassed=*/false,69/*submitted_in_package=*/false,70/*chainstate_is_current=*/true,71/*has_no_mempool_parents=*/true)};72feeEst.processTransaction(tx_info);73}74}75//Create blocks where higher fee txs are included more often76for (int h = 0; h <= blocknum%10; h++) {77// 10/10 blocks add highest fee transactions78// 9/10 blocks add 2nd highest and so on until ...79// 1/10 blocks add lowest fee transactions80while (mempool_txs[9 - h].size()) {81auto& tx_entry = mempool_txs[9 - h].back();82block_txs.emplace_back(tx_entry);83mempool_txs[9 - h].pop_back();84}85}87feeEst.processBlock(block_txs, ++blocknum);88block_txs.clear();89// Check after just a few txs that combining buckets works as expected90if (blocknum == 3) {91// At this point we should need to combine 3 buckets to get enough data points92// So estimateFee(1) should fail and estimateFee(2) should return somewhere around93// 9*baserate. estimateFee(2) %'s are 100,100,90 = average 97%94BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));95BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() < 9*baseRate.GetFeePerK() + deltaFee);96BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() > 9*baseRate.GetFeePerK() - deltaFee);97}98}100std::vector<CAmount> origFeeEst;101// Highest feerate is 10*baseRate and gets in all blocks,102// second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%,103// third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,104// so estimateFee(1) would return 10*baseRate but is hardcoded to return failure105// Second highest feerate has 100% chance of being included by 2 blocks,106// so estimateFee(2) should return 9*baseRate etc...107for (int i = 1; i < 10;i++) {108origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());109if (i > 2) { // Fee estimates should be monotonically decreasing110BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);111}112int mult = 11-i;113if (i % 2 == 0) { //At scale 2, test logic is only correct for even targets114BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);115BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);116}117}118// Fill out rest of the original estimates119for (int i = 10; i <= 48; i++) {120origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());121}123// Mine 50 more blocks with no transactions happening, estimates shouldn't change124// We haven't decayed the moving average enough so we still have enough data points in every bucket125while (blocknum < 250) {126feeEst.processBlock(block_txs, ++blocknum);127}129BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));130for (int i = 2; i < 10;i++) {131BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);132BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);133}136// Mine 15 more blocks with lots of transactions happening and not getting mined137// Estimates should go up138while (blocknum < 265) {139for (int j = 0; j < 10; j++) { // For each fee multiple140for (int k = 0; k < 4; k++) { // add 4 fee txs141tx.vin[0].prevout.n = 10000*blocknum+100*j+k;142// Simulate the tx being added to the mempool by calling processTransaction(tx_info)143mempool_txs[j].emplace_back(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));144const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));145const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),146feeV[j],147virtual_size,148entry.nHeight,149/*mempool_limit_bypassed=*/false,150/*submitted_in_package=*/false,151/*chainstate_is_current=*/true,152/*has_no_mempool_parents=*/true)};153feeEst.processTransaction(tx_info);154}155}156feeEst.processBlock(block_txs, ++blocknum);157}159for (int i = 1; i < 10;i++) {160BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);161}163// Mine all those transactions164// Estimates should still not be below original165for (int j = 0; j < 10; j++) {166while (mempool_txs[j].size()) {167auto& tx_entry = mempool_txs[j].back();168block_txs.emplace_back(tx_entry);169mempool_txs[j].pop_back();170}171}173feeEst.processBlock(block_txs, ++blocknum);174block_txs.clear();176BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));177for (int i = 2; i < 10;i++) {178BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);179}181// Mine 400 more blocks where everything is mined every block182// Estimates should be below original estimates183while (blocknum < 665) {184for (int j = 0; j < 10; j++) { // For each fee multiple185for (int k = 0; k < 4; k++) { // add 4 fee txs186tx.vin[0].prevout.n = 10000*blocknum+100*j+k;187// These txs are mined in the same block, so there is no need to188// retain them in mempool_txs; use a local entry to build block_txs.189const CTxMemPoolEntry tx_entry{entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx)};190const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));191const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),192feeV[j],193virtual_size,194entry.nHeight,195/*mempool_limit_bypassed=*/false,196/*submitted_in_package=*/false,197/*chainstate_is_current=*/true,198/*has_no_mempool_parents=*/true)};200feeEst.processTransaction(tx_info);201block_txs.emplace_back(tx_entry);202}203}205feeEst.processBlock(block_txs, ++blocknum);206block_txs.clear();207}208BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));209for (int i = 2; i < 9; i++) { // At 9, the original estimate was already at the bottom (b/c scale = 2)210BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);211}212}214BOOST_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.