LCOV - code coverage report
Current view: top level - src/test - policyestimator_tests.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 97.8 % 139 136
Test Date: 2025-08-25 05:11:47 Functions: 100.0 % 2 2
Branches: 55.2 % 402 222

             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.h>
       6                 :             : #include <policy/fees_args.h>
       7                 :             : #include <policy/policy.h>
       8                 :             : #include <test/util/txmempool.h>
       9                 :             : #include <txmempool.h>
      10                 :             : #include <uint256.h>
      11                 :             : #include <util/time.h>
      12                 :             : #include <validationinterface.h>
      13                 :             : 
      14                 :             : #include <test/util/setup_common.h>
      15                 :             : 
      16                 :             : #include <boost/test/unit_test.hpp>
      17                 :             : 
      18                 :             : BOOST_FIXTURE_TEST_SUITE(policyestimator_tests, ChainTestingSetup)
      19                 :             : 
      20   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
      21                 :             : {
      22         [ +  - ]:           1 :     CBlockPolicyEstimator feeEst{FeeestPath(*m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES};
      23   [ +  -  +  - ]:           1 :     CTxMemPool& mpool = *Assert(m_node.mempool);
      24         [ +  - ]:           1 :     m_node.validation_signals->RegisterValidationInterface(&feeEst);
      25                 :           1 :     TestMemPoolEntryHelper entry;
      26                 :           1 :     CAmount basefee(2000);
      27                 :           1 :     CAmount deltaFee(100);
      28                 :           1 :     std::vector<CAmount> feeV;
      29         [ +  - ]:           1 :     feeV.reserve(10);
      30                 :             : 
      31                 :             :     // Populate vectors of increasing fees
      32                 :          11 :     for (int j = 0; j < 10; j++) {
      33   [ +  -  +  + ]:          11 :         feeV.push_back(basefee * (j+1));
      34                 :             :     }
      35                 :             : 
      36                 :             :     // Store the hashes of transactions that have been
      37                 :             :     // added to the mempool by their associate fee
      38                 :             :     // txHashes[j] is populated with transactions either of
      39                 :             :     // fee = basefee * (j+1)
      40                 :          10 :     std::vector<Txid> txHashes[10];
      41                 :             : 
      42                 :             :     // Create a transaction template
      43                 :           1 :     CScript garbage;
      44         [ +  + ]:         129 :     for (unsigned int i = 0; i < 128; i++)
      45                 :         128 :         garbage.push_back('X');
      46         [ +  - ]:           1 :     CMutableTransaction tx;
      47         [ +  - ]:           1 :     tx.vin.resize(1);
      48                 :           1 :     tx.vin[0].scriptSig = garbage;
      49         [ +  - ]:           1 :     tx.vout.resize(1);
      50         [ +  - ]:           1 :     tx.vout[0].nValue=0LL;
      51   [ +  -  +  - ]:           2 :     CFeeRate baseRate(basefee, GetVirtualTransactionSize(CTransaction(tx)));
      52                 :             : 
      53                 :             :     // Create a fake block
      54                 :           1 :     std::vector<CTransactionRef> block;
      55                 :           1 :     int blocknum = 0;
      56                 :             : 
      57                 :             :     // Loop through 200 blocks
      58                 :             :     // At a decay .9952 and 4 fee transactions per block
      59                 :             :     // This makes the tx count about 2.5 per bucket, well above the 0.1 threshold
      60         [ +  + ]:         201 :     while (blocknum < 200) {
      61         [ +  + ]:        2200 :         for (int j = 0; j < 10; j++) { // For each fee
      62         [ +  + ]:       10000 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
      63         [ +  - ]:        8000 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique
      64                 :        8000 :                 {
      65   [ +  -  +  - ]:        8000 :                     LOCK2(cs_main, mpool.cs);
      66   [ +  -  +  - ]:        8000 :                     AddToMempool(mpool, entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
      67                 :             :                     // Since TransactionAddedToMempool callbacks are generated in ATMP,
      68                 :             :                     // not AddToMempool, we cheat and create one manually here
      69   [ +  -  +  -  :       16000 :                     const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
                   +  - ]
      70         [ +  - ]:        8000 :                     const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
      71                 :        8000 :                                                                                       feeV[j],
      72                 :             :                                                                                       virtual_size,
      73                 :             :                                                                                       entry.nHeight,
      74                 :             :                                                                                       /*mempool_limit_bypassed=*/false,
      75                 :             :                                                                                       /*submitted_in_package=*/false,
      76                 :             :                                                                                       /*chainstate_is_current=*/true,
      77   [ +  -  +  - ]:       16000 :                                                                                       /*has_no_mempool_parents=*/true)};
      78         [ +  - ]:        8000 :                     m_node.validation_signals->TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
      79   [ +  -  +  - ]:       16000 :                 }
      80         [ +  - ]:       16000 :                 txHashes[j].push_back(tx.GetHash());
      81                 :             :             }
      82                 :             :         }
      83                 :             :         //Create blocks where higher fee txs are included more often
      84         [ +  + ]:        1300 :         for (int h = 0; h <= blocknum%10; h++) {
      85                 :             :             // 10/10 blocks add highest fee transactions
      86                 :             :             // 9/10 blocks add 2nd highest and so on until ...
      87                 :             :             // 1/10 blocks add lowest fee transactions
      88   [ -  +  +  + ]:        9100 :             while (txHashes[9-h].size()) {
      89         [ +  - ]:        8000 :                 CTransactionRef ptx = mpool.get(txHashes[9-h].back());
      90         [ +  - ]:        8000 :                 if (ptx)
      91         [ +  - ]:        8000 :                     block.push_back(ptx);
      92         [ +  - ]:        8000 :                 txHashes[9-h].pop_back();
      93                 :        8000 :             }
      94                 :             :         }
      95                 :             : 
      96                 :         200 :         {
      97         [ +  - ]:         200 :             LOCK(mpool.cs);
      98         [ +  - ]:         200 :             mpool.removeForBlock(block, ++blocknum);
      99                 :           0 :         }
     100                 :             : 
     101                 :         200 :         block.clear();
     102                 :             :         // Check after just a few txs that combining buckets works as expected
     103         [ +  + ]:         200 :         if (blocknum == 3) {
     104                 :             :             // Wait for fee estimator to catch up
     105         [ +  - ]:           1 :             m_node.validation_signals->SyncWithValidationInterfaceQueue();
     106                 :             :             // At this point we should need to combine 3 buckets to get enough data points
     107                 :             :             // So estimateFee(1) should fail and estimateFee(2) should return somewhere around
     108                 :             :             // 9*baserate.  estimateFee(2) %'s are 100,100,90 = average 97%
     109   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
             +  -  +  - ]
     110   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() < 9*baseRate.GetFeePerK() + deltaFee);
             +  -  +  - ]
     111   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() > 9*baseRate.GetFeePerK() - deltaFee);
                   +  - ]
     112                 :             :         }
     113                 :             :     }
     114                 :             : 
     115                 :             :     // Wait for fee estimator to catch up
     116         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     117                 :             : 
     118                 :           1 :     std::vector<CAmount> origFeeEst;
     119                 :             :     // Highest feerate is 10*baseRate and gets in all blocks,
     120                 :             :     // second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%,
     121                 :             :     // third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,
     122                 :             :     // so estimateFee(1) would return 10*baseRate but is hardcoded to return failure
     123                 :             :     // Second highest feerate has 100% chance of being included by 2 blocks,
     124                 :             :     // so estimateFee(2) should return 9*baseRate etc...
     125         [ +  + ]:          10 :     for (int i = 1; i < 10;i++) {
     126   [ +  -  +  - ]:           9 :         origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
     127         [ +  + ]:           9 :         if (i > 2) { // Fee estimates should be monotonically decreasing
     128   [ +  -  +  - ]:          14 :             BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
     129                 :             :         }
     130                 :           9 :         int mult = 11-i;
     131         [ +  + ]:           9 :         if (i % 2 == 0) { //At scale 2, test logic is only correct for even targets
     132   [ +  -  +  -  :           8 :             BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
                   +  - ]
     133   [ +  -  +  - ]:           8 :             BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
     134                 :             :         }
     135                 :             :     }
     136                 :             :     // Fill out rest of the original estimates
     137         [ +  + ]:          40 :     for (int i = 10; i <= 48; i++) {
     138   [ +  -  +  - ]:          39 :         origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
     139                 :             :     }
     140                 :             : 
     141                 :             :     // Mine 50 more blocks with no transactions happening, estimates shouldn't change
     142                 :             :     // We haven't decayed the moving average enough so we still have enough data points in every bucket
     143         [ +  + ]:          51 :     while (blocknum < 250) {
     144         [ +  - ]:          50 :         LOCK(mpool.cs);
     145         [ +  - ]:          50 :         mpool.removeForBlock(block, ++blocknum);
     146                 :          50 :     }
     147                 :             : 
     148                 :             :     // Wait for fee estimator to catch up
     149         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     150                 :             : 
     151   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     152         [ +  + ]:           9 :     for (int i = 2; i < 10;i++) {
     153   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
             +  -  +  - ]
     154   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
                   +  - ]
     155                 :             :     }
     156                 :             : 
     157                 :             : 
     158                 :             :     // Mine 15 more blocks with lots of transactions happening and not getting mined
     159                 :             :     // Estimates should go up
     160         [ +  + ]:          16 :     while (blocknum < 265) {
     161         [ +  + ]:         165 :         for (int j = 0; j < 10; j++) { // For each fee multiple
     162         [ +  + ]:         750 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
     163         [ +  - ]:         600 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
     164                 :         600 :                 {
     165   [ +  -  +  - ]:         600 :                     LOCK2(cs_main, mpool.cs);
     166   [ +  -  +  - ]:         600 :                     AddToMempool(mpool, entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
     167                 :             :                     // Since TransactionAddedToMempool callbacks are generated in ATMP,
     168                 :             :                     // not AddToMempool, we cheat and create one manually here
     169   [ +  -  +  -  :        1200 :                     const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
                   +  - ]
     170         [ +  - ]:         600 :                     const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
     171                 :         600 :                                                                                       feeV[j],
     172                 :             :                                                                                       virtual_size,
     173                 :             :                                                                                       entry.nHeight,
     174                 :             :                                                                                       /*mempool_limit_bypassed=*/false,
     175                 :             :                                                                                       /*submitted_in_package=*/false,
     176                 :             :                                                                                       /*chainstate_is_current=*/true,
     177   [ +  -  +  - ]:        1200 :                                                                                       /*has_no_mempool_parents=*/true)};
     178         [ +  - ]:         600 :                     m_node.validation_signals->TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
     179   [ +  -  +  - ]:        1200 :                 }
     180         [ +  - ]:        1200 :                 txHashes[j].push_back(tx.GetHash());
     181                 :             :             }
     182                 :             :         }
     183                 :          15 :         {
     184         [ +  - ]:          15 :             LOCK(mpool.cs);
     185         [ +  - ]:          15 :             mpool.removeForBlock(block, ++blocknum);
     186                 :          15 :         }
     187                 :             :     }
     188                 :             : 
     189                 :             :     // Wait for fee estimator to catch up
     190         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     191                 :             : 
     192         [ +  + ]:          10 :     for (int i = 1; i < 10;i++) {
     193   [ +  -  +  -  :          18 :         BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
          -  +  -  -  -  
                -  +  - ]
     194                 :             :     }
     195                 :             : 
     196                 :             :     // Mine all those transactions
     197                 :             :     // Estimates should still not be below original
     198         [ +  + ]:          11 :     for (int j = 0; j < 10; j++) {
     199   [ -  +  +  + ]:         610 :         while(txHashes[j].size()) {
     200         [ +  - ]:         600 :             CTransactionRef ptx = mpool.get(txHashes[j].back());
     201         [ +  - ]:         600 :             if (ptx)
     202         [ +  - ]:         600 :                 block.push_back(ptx);
     203         [ +  - ]:         600 :             txHashes[j].pop_back();
     204                 :         600 :         }
     205                 :             :     }
     206                 :             : 
     207                 :           1 :     {
     208         [ +  - ]:           1 :         LOCK(mpool.cs);
     209         [ +  - ]:           1 :         mpool.removeForBlock(block, 266);
     210                 :           0 :     }
     211                 :           1 :     block.clear();
     212                 :             : 
     213                 :             :     // Wait for fee estimator to catch up
     214         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     215                 :             : 
     216   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     217         [ +  + ]:           9 :     for (int i = 2; i < 10;i++) {
     218   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
          -  +  -  -  -  
                -  +  - ]
     219                 :             :     }
     220                 :             : 
     221                 :             :     // Mine 400 more blocks where everything is mined every block
     222                 :             :     // Estimates should be below original estimates
     223         [ +  + ]:         401 :     while (blocknum < 665) {
     224         [ +  + ]:        4400 :         for (int j = 0; j < 10; j++) { // For each fee multiple
     225         [ +  + ]:       20000 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
     226         [ +  - ]:       16000 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
     227                 :       16000 :                 {
     228   [ +  -  +  - ]:       16000 :                     LOCK2(cs_main, mpool.cs);
     229   [ +  -  +  - ]:       16000 :                     AddToMempool(mpool, entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
     230                 :             :                     // Since TransactionAddedToMempool callbacks are generated in ATMP,
     231                 :             :                     // not AddToMempool, we cheat and create one manually here
     232   [ +  -  +  -  :       32000 :                     const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
                   +  - ]
     233         [ +  - ]:       16000 :                     const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
     234                 :       16000 :                                                                                       feeV[j],
     235                 :             :                                                                                       virtual_size,
     236                 :             :                                                                                       entry.nHeight,
     237                 :             :                                                                                       /*mempool_limit_bypassed=*/false,
     238                 :             :                                                                                       /*submitted_in_package=*/false,
     239                 :             :                                                                                       /*chainstate_is_current=*/true,
     240   [ +  -  +  - ]:       32000 :                                                                                       /*has_no_mempool_parents=*/true)};
     241         [ +  - ]:       16000 :                     m_node.validation_signals->TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
     242   [ +  -  +  - ]:       32000 :                 }
     243   [ +  -  +  - ]:       16000 :                 CTransactionRef ptx = mpool.get(tx.GetHash());
     244         [ +  - ]:       16000 :                 if (ptx)
     245         [ +  - ]:       16000 :                     block.push_back(ptx);
     246                 :             : 
     247                 :       16000 :             }
     248                 :             :         }
     249                 :             : 
     250                 :         400 :         {
     251         [ +  - ]:         400 :             LOCK(mpool.cs);
     252         [ +  - ]:         400 :             mpool.removeForBlock(block, ++blocknum);
     253                 :           0 :         }
     254                 :             : 
     255                 :         400 :         block.clear();
     256                 :             :     }
     257                 :             :     // Wait for fee estimator to catch up
     258         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     259   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     260         [ +  + ]:           8 :     for (int i = 2; i < 9; i++) { // At 9, the original estimate was already at the bottom (b/c scale = 2)
     261   [ +  -  +  -  :          14 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
                   +  - ]
     262                 :             :     }
     263   [ +  +  -  - ]:          13 : }
     264                 :             : 
     265                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1