LCOV - code coverage report
Current view: top level - src/test - blockpolicyestimator_tests.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 100.0 % 102 102
Test Date: 2026-08-25 06:16:57 Functions: 100.0 % 2 2
Branches: 56.8 % 322 183

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2011-present The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :             : 
       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>
      14                 :             : 
      15                 :             : #include <boost/test/unit_test.hpp>
      16                 :             : 
      17                 :             : BOOST_FIXTURE_TEST_SUITE(blockpolicyestimator_tests, ChainTestingSetup)
      18                 :             : 
      19   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
      20                 :             : {
      21         [ +  - ]:           1 :     CBlockPolicyEstimator feeEst{BlockPolicyFeeEstPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES};
      22                 :           1 :     TestMemPoolEntryHelper entry;
      23                 :           1 :     CAmount basefee(2000);
      24                 :           1 :     CAmount deltaFee(100);
      25                 :           1 :     std::vector<CAmount> feeV;
      26         [ +  - ]:           1 :     feeV.reserve(10);
      27                 :             : 
      28                 :             :     // Populate vectors of increasing fees
      29                 :          11 :     for (int j = 0; j < 10; j++) {
      30   [ +  -  +  + ]:          11 :         feeV.push_back(basefee * (j+1));
      31                 :             :     }
      32                 :             : 
      33                 :             :     // Store the hashes of transactions that have been
      34                 :             :     // added to the mempool by their associate fee
      35                 :             :     // mempool_txs[j] is populated with transactions either of
      36                 :             :     // fee = basefee * (j+1)
      37         [ +  + ]:          21 :     std::list<CTxMemPoolEntry> mempool_txs[10];
      38                 :             : 
      39                 :             :     // Create a transaction template
      40                 :           1 :     CScript garbage;
      41         [ +  + ]:         129 :     for (unsigned int i = 0; i < 128; i++)
      42                 :         128 :         garbage.push_back('X');
      43         [ +  - ]:           1 :     CMutableTransaction tx;
      44         [ +  - ]:           1 :     tx.vin.resize(1);
      45                 :           1 :     tx.vin[0].scriptSig = garbage;
      46         [ +  - ]:           1 :     tx.vout.resize(1);
      47         [ +  - ]:           1 :     tx.vout[0].nValue=0LL;
      48   [ +  -  +  - ]:           2 :     CFeeRate baseRate(basefee, GetVirtualTransactionSize(CTransaction(tx)));
      49                 :             : 
      50                 :             :     // Create a fake block
      51                 :           1 :     std::vector<RemovedMempoolTransactionInfo> block_txs;
      52                 :           1 :     int blocknum = 0;
      53                 :             : 
      54                 :             :     // Loop through 200 blocks
      55                 :             :     // At a decay .9952 and 4 fee transactions per block
      56                 :             :     // This makes the tx count about 2.5 per bucket, well above the 0.1 threshold
      57         [ +  + ]:         201 :     while (blocknum < 200) {
      58         [ +  + ]:        2200 :         for (int j = 0; j < 10; j++) { // For each fee
      59         [ +  + ]:       10000 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
      60                 :        8000 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique
      61                 :             :                 // Simulate the tx being added to the mempool by calling processTransaction(tx_info)
      62   [ +  -  +  - ]:        8000 :                 mempool_txs[j].emplace_back(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
      63   [ +  -  +  -  :       16000 :                 const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
                   +  - ]
      64         [ +  - ]:        8000 :                 const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
      65                 :        8000 :                                                                                   feeV[j],
      66                 :             :                                                                                   virtual_size,
      67                 :             :                                                                                   entry.nHeight,
      68                 :             :                                                                                   /*mempool_limit_bypassed=*/false,
      69                 :             :                                                                                   /*submitted_in_package=*/false,
      70                 :             :                                                                                   /*chainstate_is_current=*/true,
      71   [ +  -  +  - ]:       16000 :                                                                                   /*has_no_mempool_parents=*/true)};
      72         [ +  - ]:        8000 :                 feeEst.processTransaction(tx_info);
      73                 :        8000 :             }
      74                 :             :         }
      75                 :             :         //Create blocks where higher fee txs are included more often
      76         [ +  + ]:        1300 :         for (int h = 0; h <= blocknum%10; h++) {
      77                 :             :             // 10/10 blocks add highest fee transactions
      78                 :             :             // 9/10 blocks add 2nd highest and so on until ...
      79                 :             :             // 1/10 blocks add lowest fee transactions
      80         [ +  + ]:        9100 :             while (mempool_txs[9 - h].size()) {
      81         [ +  - ]:        8000 :                 auto& tx_entry = mempool_txs[9 - h].back();
      82         [ +  - ]:        8000 :                 block_txs.emplace_back(tx_entry);
      83                 :        8000 :                 mempool_txs[9 - h].pop_back();
      84                 :             :             }
      85                 :             :         }
      86                 :             : 
      87         [ +  - ]:         200 :         feeEst.processBlock(block_txs, ++blocknum);
      88                 :         200 :         block_txs.clear();
      89                 :             :         // Check after just a few txs that combining buckets works as expected
      90         [ +  + ]:         200 :         if (blocknum == 3) {
      91                 :             :             // At this point we should need to combine 3 buckets to get enough data points
      92                 :             :             // So estimateFee(1) should fail and estimateFee(2) should return somewhere around
      93                 :             :             // 9*baserate.  estimateFee(2) %'s are 100,100,90 = average 97%
      94   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
             +  -  +  - ]
      95   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() < 9*baseRate.GetFeePerK() + deltaFee);
             +  -  +  - ]
      96   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() > 9*baseRate.GetFeePerK() - deltaFee);
                   +  - ]
      97                 :             :         }
      98                 :             :     }
      99                 :             : 
     100                 :           1 :     std::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 failure
     105                 :             :     // Second highest feerate has 100% chance of being included by 2 blocks,
     106                 :             :     // so estimateFee(2) should return 9*baseRate etc...
     107         [ +  + ]:          10 :     for (int i = 1; i < 10;i++) {
     108   [ +  -  +  - ]:           9 :         origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
     109         [ +  + ]:           9 :         if (i > 2) { // Fee estimates should be monotonically decreasing
     110   [ +  -  +  - ]:          14 :             BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
     111                 :             :         }
     112                 :           9 :         int mult = 11-i;
     113         [ +  + ]:           9 :         if (i % 2 == 0) { //At scale 2, test logic is only correct for even targets
     114   [ +  -  +  -  :           8 :             BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
                   +  - ]
     115   [ +  -  +  - ]:           8 :             BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
     116                 :             :         }
     117                 :             :     }
     118                 :             :     // Fill out rest of the original estimates
     119         [ +  + ]:          40 :     for (int i = 10; i <= 48; i++) {
     120   [ +  -  +  - ]:          39 :         origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
     121                 :             :     }
     122                 :             : 
     123                 :             :     // Mine 50 more blocks with no transactions happening, estimates shouldn't change
     124                 :             :     // We haven't decayed the moving average enough so we still have enough data points in every bucket
     125         [ +  + ]:          51 :     while (blocknum < 250) {
     126         [ +  - ]:          50 :         feeEst.processBlock(block_txs, ++blocknum);
     127                 :             :     }
     128                 :             : 
     129   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     130         [ +  + ]:           9 :     for (int i = 2; i < 10;i++) {
     131   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
             +  -  +  - ]
     132   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
                   +  - ]
     133                 :             :     }
     134                 :             : 
     135                 :             : 
     136                 :             :     // Mine 15 more blocks with lots of transactions happening and not getting mined
     137                 :             :     // Estimates should go up
     138         [ +  + ]:          16 :     while (blocknum < 265) {
     139         [ +  + ]:         165 :         for (int j = 0; j < 10; j++) { // For each fee multiple
     140         [ +  + ]:         750 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
     141                 :         600 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
     142                 :             :                 // Simulate the tx being added to the mempool by calling processTransaction(tx_info)
     143   [ +  -  +  - ]:         600 :                 mempool_txs[j].emplace_back(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
     144   [ +  -  +  -  :        1200 :                 const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
                   +  - ]
     145         [ +  - ]:         600 :                 const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
     146                 :         600 :                                                                                   feeV[j],
     147                 :             :                                                                                   virtual_size,
     148                 :             :                                                                                   entry.nHeight,
     149                 :             :                                                                                   /*mempool_limit_bypassed=*/false,
     150                 :             :                                                                                   /*submitted_in_package=*/false,
     151                 :             :                                                                                   /*chainstate_is_current=*/true,
     152   [ +  -  +  - ]:        1200 :                                                                                   /*has_no_mempool_parents=*/true)};
     153         [ +  - ]:         600 :                 feeEst.processTransaction(tx_info);
     154                 :         600 :             }
     155                 :             :         }
     156         [ +  - ]:          15 :         feeEst.processBlock(block_txs, ++blocknum);
     157                 :             :     }
     158                 :             : 
     159         [ +  + ]:          10 :     for (int i = 1; i < 10;i++) {
     160   [ +  -  +  -  :          18 :         BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
          -  +  -  -  -  
                -  +  - ]
     161                 :             :     }
     162                 :             : 
     163                 :             :     // Mine all those transactions
     164                 :             :     // Estimates should still not be below original
     165         [ +  + ]:          11 :     for (int j = 0; j < 10; j++) {
     166         [ +  + ]:         610 :         while (mempool_txs[j].size()) {
     167         [ +  - ]:         600 :             auto& tx_entry = mempool_txs[j].back();
     168         [ +  - ]:         600 :             block_txs.emplace_back(tx_entry);
     169                 :         600 :             mempool_txs[j].pop_back();
     170                 :             :         }
     171                 :             :     }
     172                 :             : 
     173         [ +  - ]:           1 :     feeEst.processBlock(block_txs, ++blocknum);
     174                 :           1 :     block_txs.clear();
     175                 :             : 
     176   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     177         [ +  + ]:           9 :     for (int i = 2; i < 10;i++) {
     178   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
          -  +  -  -  -  
                -  +  - ]
     179                 :             :     }
     180                 :             : 
     181                 :             :     // Mine 400 more blocks where everything is mined every block
     182                 :             :     // Estimates should be below original estimates
     183         [ +  + ]:         400 :     while (blocknum < 665) {
     184         [ +  + ]:        4389 :         for (int j = 0; j < 10; j++) { // For each fee multiple
     185         [ +  + ]:       19950 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
     186                 :       15960 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
     187                 :             :                 // These txs are mined in the same block, so there is no need to
     188                 :             :                 // retain them in mempool_txs; use a local entry to build block_txs.
     189         [ +  - ]:       15960 :                 const CTxMemPoolEntry tx_entry{entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx)};
     190   [ +  -  +  -  :       31920 :                 const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
                   +  - ]
     191         [ +  - ]:       15960 :                 const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
     192                 :       15960 :                                                                                   feeV[j],
     193                 :             :                                                                                   virtual_size,
     194                 :             :                                                                                   entry.nHeight,
     195                 :             :                                                                                   /*mempool_limit_bypassed=*/false,
     196                 :             :                                                                                   /*submitted_in_package=*/false,
     197                 :             :                                                                                   /*chainstate_is_current=*/true,
     198   [ +  -  +  - ]:       31920 :                                                                                   /*has_no_mempool_parents=*/true)};
     199                 :             : 
     200         [ +  - ]:       15960 :                 feeEst.processTransaction(tx_info);
     201         [ +  - ]:       15960 :                 block_txs.emplace_back(tx_entry);
     202                 :       15960 :             }
     203                 :             :         }
     204                 :             : 
     205         [ +  - ]:         399 :         feeEst.processBlock(block_txs, ++blocknum);
     206                 :         399 :         block_txs.clear();
     207                 :             :     }
     208   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     209         [ +  + ]:           8 :     for (int i = 2; i < 9; i++) { // At 9, the original estimate was already at the bottom (b/c scale = 2)
     210   [ +  -  +  -  :          14 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
                   +  - ]
     211                 :             :     }
     212   [ +  +  -  - ]:          13 : }
     213                 :             : 
     214                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1