LCOV - code coverage report
Current view: top level - src/test - policyestimator_tests.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 116 116 100.0 %
Date: 2026-06-25 07:23:51 Functions: 10 10 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2011-2020 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/policy.h>
       7             : #include <test/util/txmempool.h>
       8             : #include <txmempool.h>
       9             : #include <uint256.h>
      10             : #include <util/time.h>
      11             : 
      12             : #include <test/util/setup_common.h>
      13             : 
      14             : #include <boost/test/unit_test.hpp>
      15             : 
      16         146 : BOOST_FIXTURE_TEST_SUITE(policyestimator_tests, ChainTestingSetup)
      17             : 
      18         149 : BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
      19             : {
      20           1 :     CBlockPolicyEstimator& feeEst = *Assert(m_node.fee_estimator);
      21           1 :     CTxMemPool& mpool = *Assert(m_node.mempool);
      22           1 :     LOCK2(cs_main, mpool.cs);
      23           1 :     TestMemPoolEntryHelper entry;
      24           1 :     CAmount basefee(2000);
      25           1 :     CAmount deltaFee(100);
      26           1 :     std::vector<CAmount> feeV;
      27             : 
      28             :     // Populate vectors of increasing fees
      29          11 :     for (int j = 0; j < 10; j++) {
      30          10 :         feeV.push_back(basefee * (j+1));
      31          10 :     }
      32             : 
      33             :     // Store the hashes of transactions that have been
      34             :     // added to the mempool by their associate fee
      35             :     // txHashes[j] is populated with transactions either of
      36             :     // fee = basefee * (j+1)
      37           1 :     std::vector<uint256> txHashes[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           1 :     CFeeRate baseRate(basefee, GetVirtualTransactionSize(CTransaction(tx)));
      49             : 
      50             :     // Create a fake block
      51           1 :     std::vector<CTransactionRef> block;
      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        8000 :                 uint256 hash = tx.GetHash();
      62        8000 :                 mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
      63        8000 :                 txHashes[j].push_back(hash);
      64        8000 :             }
      65        2000 :         }
      66             :         //Create blocks where higher fee txs are included more often
      67        1300 :         for (int h = 0; h <= blocknum%10; h++) {
      68             :             // 10/10 blocks add highest fee transactions
      69             :             // 9/10 blocks add 2nd highest and so on until ...
      70             :             // 1/10 blocks add lowest fee transactions
      71        9100 :             while (txHashes[9-h].size()) {
      72        8000 :                 CTransactionRef ptx = mpool.get(txHashes[9-h].back());
      73        8000 :                 if (ptx)
      74        8000 :                     block.push_back(ptx);
      75        8000 :                 txHashes[9-h].pop_back();
      76        8000 :             }
      77        1100 :         }
      78         200 :         mpool.removeForBlock(block, ++blocknum);
      79         200 :         block.clear();
      80             :         // Check after just a few txs that combining buckets works as expected
      81         200 :         if (blocknum == 3) {
      82             :             // At this point we should need to combine 3 buckets to get enough data points
      83             :             // So estimateFee(1) should fail and estimateFee(2) should return somewhere around
      84             :             // 9*baserate.  estimateFee(2) %'s are 100,100,90 = average 97%
      85           1 :             BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
      86           1 :             BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() < 9*baseRate.GetFeePerK() + deltaFee);
      87           1 :             BOOST_CHECK(feeEst.estimateFee(2).GetFeePerK() > 9*baseRate.GetFeePerK() - deltaFee);
      88           1 :         }
      89             :     }
      90             : 
      91           1 :     std::vector<CAmount> origFeeEst;
      92             :     // Highest feerate is 10*baseRate and gets in all blocks,
      93             :     // second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%,
      94             :     // third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,
      95             :     // so estimateFee(1) would return 10*baseRate but is hardcoded to return failure
      96             :     // Second highest feerate has 100% chance of being included by 2 blocks,
      97             :     // so estimateFee(2) should return 9*baseRate etc...
      98          10 :     for (int i = 1; i < 10;i++) {
      99           9 :         origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
     100           9 :         if (i > 2) { // Fee estimates should be monotonically decreasing
     101           7 :             BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
     102           7 :         }
     103           9 :         int mult = 11-i;
     104           9 :         if (i % 2 == 0) { //At scale 2, test logic is only correct for even targets
     105           4 :             BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
     106           4 :             BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
     107           4 :         }
     108           9 :     }
     109             :     // Fill out rest of the original estimates
     110          40 :     for (int i = 10; i <= 48; i++) {
     111          39 :         origFeeEst.push_back(feeEst.estimateFee(i).GetFeePerK());
     112          39 :     }
     113             : 
     114             :     // Mine 50 more blocks with no transactions happening, estimates shouldn't change
     115             :     // We haven't decayed the moving average enough so we still have enough data points in every bucket
     116          51 :     while (blocknum < 250)
     117          50 :         mpool.removeForBlock(block, ++blocknum);
     118             : 
     119           1 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
     120           9 :     for (int i = 2; i < 10;i++) {
     121           8 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] + deltaFee);
     122           8 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
     123           8 :     }
     124             : 
     125             : 
     126             :     // Mine 15 more blocks with lots of transactions happening and not getting mined
     127             :     // Estimates should go up
     128          16 :     while (blocknum < 265) {
     129         165 :         for (int j = 0; j < 10; j++) { // For each fee multiple
     130         750 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
     131         600 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
     132         600 :                 uint256 hash = tx.GetHash();
     133         600 :                 mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
     134         600 :                 txHashes[j].push_back(hash);
     135         600 :             }
     136         150 :         }
     137          15 :         mpool.removeForBlock(block, ++blocknum);
     138             :     }
     139             : 
     140          10 :     for (int i = 1; i < 10;i++) {
     141           9 :         BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
     142           9 :     }
     143             : 
     144             :     // Mine all those transactions
     145             :     // Estimates should still not be below original
     146          11 :     for (int j = 0; j < 10; j++) {
     147         610 :         while(txHashes[j].size()) {
     148         600 :             CTransactionRef ptx = mpool.get(txHashes[j].back());
     149         600 :             if (ptx)
     150         600 :                 block.push_back(ptx);
     151         600 :             txHashes[j].pop_back();
     152         600 :         }
     153          10 :     }
     154           1 :     mpool.removeForBlock(block, 266);
     155           1 :     block.clear();
     156           1 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
     157           9 :     for (int i = 2; i < 10;i++) {
     158           8 :         BOOST_CHECK(feeEst.estimateFee(i) == CFeeRate(0) || feeEst.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
     159           8 :     }
     160             : 
     161             :     // Mine 400 more blocks where everything is mined every block
     162             :     // Estimates should be below original estimates
     163         401 :     while (blocknum < 665) {
     164        4400 :         for (int j = 0; j < 10; j++) { // For each fee multiple
     165       20000 :             for (int k = 0; k < 4; k++) { // add 4 fee txs
     166       16000 :                 tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
     167       16000 :                 uint256 hash = tx.GetHash();
     168       16000 :                 mpool.addUnchecked(entry.Fee(feeV[j]).Time(Now<NodeSeconds>()).Height(blocknum).FromTx(tx));
     169       16000 :                 CTransactionRef ptx = mpool.get(hash);
     170       16000 :                 if (ptx)
     171       16000 :                     block.push_back(ptx);
     172             : 
     173       16000 :             }
     174        4000 :         }
     175         400 :         mpool.removeForBlock(block, ++blocknum);
     176         400 :         block.clear();
     177             :     }
     178           1 :     BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
     179           8 :     for (int i = 2; i < 9; i++) { // At 9, the original estimate was already at the bottom (b/c scale = 2)
     180           7 :         BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
     181           7 :     }
     182           1 : }
     183             : 
     184         146 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.16