LCOV - code coverage report
Current view: top level - src/test - policyestimator_tests.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 97.9 % 142 139
Test Date: 2024-08-28 04:44:32 Functions: 100.0 % 2 2
Branches: 55.4 % 392 217

             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<uint256> 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 :                     mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
      67                 :             :                     // Since TransactionAddedToMempool callbacks are generated in ATMP,
      68                 :             :                     // not addUnchecked, 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         [ +  - ]:        8000 :                 uint256 hash = tx.GetHash();
      81         [ +  - ]:        8000 :                 txHashes[j].push_back(hash);
      82                 :             :             }
      83                 :             :         }
      84                 :             :         //Create blocks where higher fee txs are included more often
      85         [ +  + ]:        1300 :         for (int h = 0; h <= blocknum%10; h++) {
      86                 :             :             // 10/10 blocks add highest fee transactions
      87                 :             :             // 9/10 blocks add 2nd highest and so on until ...
      88                 :             :             // 1/10 blocks add lowest fee transactions
      89         [ +  + ]:        9100 :             while (txHashes[9-h].size()) {
      90         [ +  - ]:        8000 :                 CTransactionRef ptx = mpool.get(txHashes[9-h].back());
      91         [ +  - ]:        8000 :                 if (ptx)
      92         [ +  - ]:        8000 :                     block.push_back(ptx);
      93         [ +  - ]:        8000 :                 txHashes[9-h].pop_back();
      94                 :        8000 :             }
      95                 :             :         }
      96                 :             : 
      97                 :         200 :         {
      98         [ +  - ]:         200 :             LOCK(mpool.cs);
      99         [ +  - ]:         200 :             mpool.removeForBlock(block, ++blocknum);
     100                 :           0 :         }
     101                 :             : 
     102                 :         200 :         block.clear();
     103                 :             :         // Check after just a few txs that combining buckets works as expected
     104         [ +  + ]:         200 :         if (blocknum == 3) {
     105                 :             :             // Wait for fee estimator to catch up
     106         [ +  - ]:           1 :             m_node.validation_signals->SyncWithValidationInterfaceQueue();
     107                 :             :             // At this point we should need to combine 3 buckets to get enough data points
     108                 :             :             // So estimateFee(1) should fail and estimateFee(2) should return somewhere around
     109                 :             :             // 9*baserate.  estimateFee(2) %'s are 100,100,90 = average 97%
     110   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
             +  -  +  - ]
     111   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() < 9*baseRate.GetFeePerK() + deltaFee);
             +  -  +  - ]
     112   [ +  -  +  -  :           2 :             BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() > 9*baseRate.GetFeePerK() - deltaFee);
                   +  - ]
     113                 :             :         }
     114                 :             :     }
     115                 :             : 
     116                 :             :     // Wait for fee estimator to catch up
     117         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     118                 :             : 
     119                 :           1 :     std::vector<CAmount> origFeeEst;
     120                 :             :     // Highest feerate is 10*baseRate and gets in all blocks,
     121                 :             :     // second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%,
     122                 :             :     // third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,
     123                 :             :     // so estimateFee(1) would return 10*baseRate but is hardcoded to return failure
     124                 :             :     // Second highest feerate has 100% chance of being included by 2 blocks,
     125                 :             :     // so estimateFee(2) should return 9*baseRate etc...
     126         [ +  + ]:          10 :     for (int i = 1; i < 10;i++) {
     127   [ +  -  +  - ]:           9 :         origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
     128         [ +  + ]:           9 :         if (i > 2) { // Fee estimates should be monotonically decreasing
     129   [ +  -  +  - ]:          14 :             BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
     130                 :             :         }
     131                 :           9 :         int mult = 11-i;
     132         [ +  + ]:           9 :         if (i % 2 == 0) { //At scale 2, test logic is only correct for even targets
     133   [ +  -  +  -  :           8 :             BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
                   +  - ]
     134   [ +  -  +  - ]:           8 :             BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
     135                 :             :         }
     136                 :             :     }
     137                 :             :     // Fill out rest of the original estimates
     138         [ +  + ]:          40 :     for (int i = 10; i <= 48; i++) {
     139   [ +  -  +  - ]:          39 :         origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
     140                 :             :     }
     141                 :             : 
     142                 :             :     // Mine 50 more blocks with no transactions happening, estimates shouldn't change
     143                 :             :     // We haven't decayed the moving average enough so we still have enough data points in every bucket
     144         [ +  + ]:          51 :     while (blocknum < 250) {
     145         [ +  - ]:          50 :         LOCK(mpool.cs);
     146         [ +  - ]:          50 :         mpool.removeForBlock(block, ++blocknum);
     147                 :          50 :     }
     148                 :             : 
     149                 :             :     // Wait for fee estimator to catch up
     150         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     151                 :             : 
     152   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     153         [ +  + ]:           9 :     for (int i = 2; i < 10;i++) {
     154   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
             +  -  +  - ]
     155   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
                   +  - ]
     156                 :             :     }
     157                 :             : 
     158                 :             : 
     159                 :             :     // Mine 15 more blocks with lots of transactions happening and not getting mined
     160                 :             :     // Estimates should go up
     161         [ +  + ]:          16 :     while (blocknum < 265) {
     162         [ +  + ]:         165 :         for (int j = 0; j < 10; j++) { // For each fee multiple
     163         [ +  + ]:         750 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
     164         [ +  - ]:         600 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
     165                 :         600 :                 {
     166   [ +  -  +  - ]:         600 :                     LOCK2(cs_main, mpool.cs);
     167   [ +  -  +  - ]:         600 :                     mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
     168                 :             :                     // Since TransactionAddedToMempool callbacks are generated in ATMP,
     169                 :             :                     // not addUnchecked, we cheat and create one manually here
     170   [ +  -  +  -  :        1200 :                     const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
                   +  - ]
     171         [ +  - ]:         600 :                     const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
     172                 :         600 :                                                                                       feeV[j],
     173                 :             :                                                                                       virtual_size,
     174                 :             :                                                                                       entry.nHeight,
     175                 :             :                                                                                       /*mempool_limit_bypassed=*/false,
     176                 :             :                                                                                       /*submitted_in_package=*/false,
     177                 :             :                                                                                       /*chainstate_is_current=*/true,
     178   [ +  -  +  - ]:        1200 :                                                                                       /*has_no_mempool_parents=*/true)};
     179         [ +  - ]:         600 :                     m_node.validation_signals->TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
     180   [ +  -  +  - ]:        1200 :                 }
     181         [ +  - ]:         600 :                 uint256 hash = tx.GetHash();
     182         [ +  - ]:         600 :                 txHashes[j].push_back(hash);
     183                 :             :             }
     184                 :             :         }
     185                 :          15 :         {
     186         [ +  - ]:          15 :             LOCK(mpool.cs);
     187         [ +  - ]:          15 :             mpool.removeForBlock(block, ++blocknum);
     188                 :          15 :         }
     189                 :             :     }
     190                 :             : 
     191                 :             :     // Wait for fee estimator to catch up
     192         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     193                 :             : 
     194         [ +  + ]:          10 :     for (int i = 1; i < 10;i++) {
     195   [ +  -  +  -  :          18 :         BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
          -  +  -  -  -  
                -  +  - ]
     196                 :             :     }
     197                 :             : 
     198                 :             :     // Mine all those transactions
     199                 :             :     // Estimates should still not be below original
     200         [ +  + ]:          11 :     for (int j = 0; j < 10; j++) {
     201         [ +  + ]:         610 :         while(txHashes[j].size()) {
     202         [ +  - ]:         600 :             CTransactionRef ptx = mpool.get(txHashes[j].back());
     203         [ +  - ]:         600 :             if (ptx)
     204         [ +  - ]:         600 :                 block.push_back(ptx);
     205         [ +  - ]:         600 :             txHashes[j].pop_back();
     206                 :         600 :         }
     207                 :             :     }
     208                 :             : 
     209                 :           1 :     {
     210         [ +  - ]:           1 :         LOCK(mpool.cs);
     211         [ +  - ]:           1 :         mpool.removeForBlock(block, 266);
     212                 :           0 :     }
     213                 :           1 :     block.clear();
     214                 :             : 
     215                 :             :     // Wait for fee estimator to catch up
     216         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     217                 :             : 
     218   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     219         [ +  + ]:           9 :     for (int i = 2; i < 10;i++) {
     220   [ +  -  +  -  :          16 :         BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
          -  +  -  -  -  
                -  +  - ]
     221                 :             :     }
     222                 :             : 
     223                 :             :     // Mine 400 more blocks where everything is mined every block
     224                 :             :     // Estimates should be below original estimates
     225         [ +  + ]:         401 :     while (blocknum < 665) {
     226         [ +  + ]:        4400 :         for (int j = 0; j < 10; j++) { // For each fee multiple
     227         [ +  + ]:       20000 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
     228         [ +  - ]:       16000 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
     229                 :       16000 :                 {
     230   [ +  -  +  - ]:       16000 :                     LOCK2(cs_main, mpool.cs);
     231   [ +  -  +  - ]:       16000 :                     mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
     232                 :             :                     // Since TransactionAddedToMempool callbacks are generated in ATMP,
     233                 :             :                     // not addUnchecked, we cheat and create one manually here
     234   [ +  -  +  -  :       32000 :                     const int64_t virtual_size = GetVirtualTransactionSize(*MakeTransactionRef(tx));
                   +  - ]
     235         [ +  - ]:       16000 :                     const NewMempoolTransactionInfo tx_info{NewMempoolTransactionInfo(MakeTransactionRef(tx),
     236                 :       16000 :                                                                                       feeV[j],
     237                 :             :                                                                                       virtual_size,
     238                 :             :                                                                                       entry.nHeight,
     239                 :             :                                                                                       /*mempool_limit_bypassed=*/false,
     240                 :             :                                                                                       /*submitted_in_package=*/false,
     241                 :             :                                                                                       /*chainstate_is_current=*/true,
     242   [ +  -  +  - ]:       32000 :                                                                                       /*has_no_mempool_parents=*/true)};
     243         [ +  - ]:       16000 :                     m_node.validation_signals->TransactionAddedToMempool(tx_info, mpool.GetAndIncrementSequence());
     244   [ +  -  +  - ]:       32000 :                 }
     245         [ +  - ]:       16000 :                 uint256 hash = tx.GetHash();
     246         [ +  - ]:       16000 :                 CTransactionRef ptx = mpool.get(hash);
     247         [ +  - ]:       16000 :                 if (ptx)
     248         [ +  - ]:       16000 :                     block.push_back(ptx);
     249                 :             : 
     250                 :       16000 :             }
     251                 :             :         }
     252                 :             : 
     253                 :         400 :         {
     254         [ +  - ]:         400 :             LOCK(mpool.cs);
     255         [ +  - ]:         400 :             mpool.removeForBlock(block, ++blocknum);
     256                 :           0 :         }
     257                 :             : 
     258                 :         400 :         block.clear();
     259                 :             :     }
     260                 :             :     // Wait for fee estimator to catch up
     261         [ +  - ]:           1 :     m_node.validation_signals->SyncWithValidationInterfaceQueue();
     262   [ +  -  +  -  :           2 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
                   +  - ]
     263         [ +  + ]:           8 :     for (int i = 2; i < 9; i++) { // At 9, the original estimate was already at the bottom (b/c scale = 2)
     264   [ +  -  +  -  :          14 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
                   +  - ]
     265                 :             :     }
     266   [ +  +  -  - ]:          13 : }
     267                 :             : 
     268                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1