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()
|