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