Branch data Line data Source code
1 : : // Copyright (c) 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 <kernel/mempool_entry.h>
6 : : #include <policy/fees/estimator_args.h>
7 : : #include <policy/fees/mempool_estimator.h>
8 : : #include <policy/policy.h>
9 : : #include <primitives/block.h>
10 : : #include <random.h>
11 : : #include <test/util/setup_common.h>
12 : : #include <test/util/txmempool.h>
13 : : #include <txmempool.h>
14 : : #include <uint256.h>
15 : : #include <util/feefrac.h>
16 : : #include <util/fees.h>
17 : : #include <util/time.h>
18 : : #include <validation.h>
19 : :
20 : : #include <boost/test/unit_test.hpp>
21 : :
22 : : #include <string>
23 : :
24 : : BOOST_FIXTURE_TEST_SUITE(mempool_fee_estimator_tests, TestingSetup)
25 : :
26 : 186335 : static inline CTransactionRef MakeRandomTx()
27 : : {
28 : 186335 : auto rng = FastRandomContext();
29 [ + - ]: 186335 : auto tx = CMutableTransaction();
30 [ + - ]: 186335 : tx.vin.resize(1);
31 [ + - ]: 186335 : tx.vout.resize(1);
32 [ + - ]: 186335 : tx.vin[0].prevout.hash = Txid::FromUint256(rng.rand256());
33 : 186335 : tx.vin[0].prevout.n = 0;
34 [ + - ]: 186335 : tx.vin[0].scriptSig << OP_TRUE;
35 [ + - ]: 186335 : tx.vout[0].scriptPubKey = CScript() << OP_TRUE;
36 [ + - ]: 186335 : tx.vout[0].nValue = COIN;
37 [ + - ]: 372670 : return MakeTransactionRef(tx);
38 : 186335 : }
39 : :
40 : 28 : void AddRemovedBlock(MemPoolFeeRateEstimator& fee_est,
41 : : int32_t removed_txs_weight,
42 : : int32_t block_txs_weight,
43 : : unsigned int& height)
44 : : {
45 : 28 : auto block = std::make_shared<CBlock>();
46 : 28 : std::vector<RemovedMempoolTransactionInfo> removed_txs;
47 : 28 : TestMemPoolEntryHelper entry;
48 [ - + ]: 28 : Assert(block_txs_weight >= removed_txs_weight);
49 [ + - + - ]: 28 : block->vtx.emplace_back(MakeRandomTx()); // Add a coinbase tx
50 [ + + ]: 169376 : while (block_txs_weight > 0) {
51 [ + - ]: 169370 : auto tx = MakeRandomTx();
52 : 169370 : auto tx_weight = GetTransactionWeight(*tx);
53 [ + + ]: 169370 : if (block_txs_weight - tx_weight < 0) break;
54 [ + - ]: 169348 : block->vtx.emplace_back(tx);
55 : 169348 : block_txs_weight -= tx_weight;
56 [ + + ]: 169348 : if (removed_txs_weight - tx_weight >= 0) {
57 [ + - + - ]: 157250 : removed_txs.emplace_back(entry.FromTx(tx));
58 : 157250 : removed_txs_weight -= tx_weight;
59 : : }
60 : 169370 : }
61 [ + - + - ]: 56 : fee_est.MempoolTxsRemovedForBlock(block, removed_txs, height);
62 : 28 : height += 1;
63 [ + - ]: 56 : }
64 : :
65 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(calculate_max_weight_percentiles)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
66 : : {
67 : : // With no chunks neither percentile can be populated.
68 : 1 : const auto empty = MemPoolFeeRateEstimator::CalculateMaxWeightPercentiles({});
69 [ + - ]: 2 : BOOST_CHECK(empty.p50.IsEmpty());
70 [ + - + - ]: 2 : BOOST_CHECK(empty.p75.IsEmpty());
71 : 1 : const int32_t chunk_size{10};
72 : 1 : const int32_t individual_tx_vsize = static_cast<int32_t>(DEFAULT_BLOCK_MAX_WEIGHT / WITNESS_SCALE_FACTOR) / chunk_size;
73 : 1 : const FeePerVSize super_high_fee_rate{500 * individual_tx_vsize, individual_tx_vsize};
74 : 1 : const FeePerVSize high_fee_rate{100 * individual_tx_vsize, individual_tx_vsize};
75 : 1 : const FeePerVSize medium_fee_rate{50 * individual_tx_vsize, individual_tx_vsize};
76 : 1 : const FeePerVSize low_fee_rate{10 * individual_tx_vsize, individual_tx_vsize};
77 : 1 : std::vector<FeePerVSize> chunk_feerates;
78 [ + - ]: 1 : chunk_feerates.reserve(chunk_size);
79 [ + + ]: 11 : for (int i = 0; i < chunk_size; ++i) {
80 [ + + ]: 10 : if (i < 3) {
81 [ + - ]: 3 : chunk_feerates.emplace_back(super_high_fee_rate);
82 [ + + ]: 7 : } else if (i < 5) {
83 [ + - ]: 2 : chunk_feerates.emplace_back(high_fee_rate);
84 [ + + ]: 5 : } else if (i < 8) {
85 [ + - ]: 3 : chunk_feerates.emplace_back(medium_fee_rate);
86 : : // Once 50% coverage is reached but 75% is not, only the p50 (conservative)
87 : : // percentile is populated; p75 (economical) is left empty for the caller to floor.
88 [ + + ]: 3 : if (i < 7) {
89 [ - + + - ]: 2 : const auto partial = MemPoolFeeRateEstimator::CalculateMaxWeightPercentiles(chunk_feerates);
90 [ + - + - ]: 2 : BOOST_CHECK_EQUAL(partial.p50.fee, high_fee_rate.fee);
91 [ + - + - ]: 2 : BOOST_CHECK_EQUAL(partial.p50.size, high_fee_rate.size);
92 [ + - + - ]: 4 : BOOST_CHECK(partial.p75.IsEmpty());
93 : : }
94 : : } else {
95 [ + - ]: 2 : chunk_feerates.emplace_back(low_fee_rate);
96 : : }
97 : : }
98 [ - + + - ]: 1 : const auto percentiles = MemPoolFeeRateEstimator::CalculateMaxWeightPercentiles(chunk_feerates);
99 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(percentiles.p50.fee, high_fee_rate.fee);
100 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(percentiles.p50.size, high_fee_rate.size);
101 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(percentiles.p75.fee, medium_fee_rate.fee);
102 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(percentiles.p75.size, medium_fee_rate.size);
103 [ + - + - ]: 2 : BOOST_CHECK(ByRatio{percentiles.p50} > ByRatio{percentiles.p75});
104 : 1 : }
105 : :
106 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(mempool_fee_rate_estimator_cache)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
107 : : {
108 : 1 : FakeNodeClock clock{};
109 : 1 : MemPoolFeeRateEstimatorCache cache;
110 : 1 : const uint256 tip_hash{uint256::ONE};
111 : 1 : const uint256 next_tip_hash{uint256{2}};
112 : 1 : const FeePerVSize conservative{2, 1};
113 : 1 : const FeePerVSize economical{1, 1};
114 : :
115 [ + - + - : 2 : BOOST_CHECK(cache.IsStale());
+ - + - ]
116 [ + - + - : 2 : BOOST_CHECK(!cache.GetCachedEstimate(tip_hash));
+ - + - ]
117 : :
118 [ + - ]: 1 : cache.Update(conservative, economical, tip_hash);
119 [ + - + - : 2 : BOOST_CHECK(!cache.IsStale());
+ - + - ]
120 [ + - ]: 1 : const auto cached{cache.GetCachedEstimate(tip_hash)};
121 [ + - + - : 2 : BOOST_REQUIRE(cached);
+ - ]
122 [ + - + - : 3 : BOOST_CHECK(cached->m_conservative == conservative);
+ - + - ]
123 [ + - + - : 3 : BOOST_CHECK(cached->m_economical == economical);
+ - + - ]
124 [ + - + - : 2 : BOOST_CHECK(!cache.GetCachedEstimate(next_tip_hash));
+ - + - ]
125 : :
126 [ + - ]: 1 : clock += CACHE_LIFE + std::chrono::seconds{1};
127 [ + - + - : 2 : BOOST_CHECK(cache.IsStale());
+ - + - ]
128 [ + - + - : 2 : BOOST_CHECK(!cache.GetCachedEstimate(tip_hash));
+ - ]
129 : 1 : }
130 : :
131 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(MempoolFeeRateEstimator)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
132 : : {
133 [ + - ]: 1 : auto mempool_estimator = MemPoolFeeRateEstimator(MempoolPolicyEstimatorPath(*m_node.args), *m_node.mempool, *m_node.chainman);
134 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(mempool_estimator.MaximumTarget(), MEMPOOL_FEE_ESTIMATOR_MAX_TARGET);
135 : : // Before the mempool has finished loading, no estimate is available.
136 : 1 : {
137 : 1 : const std::string unloaded_err = strprintf("%s: Mempool not loaded yet, no fee rate estimate available",
138 [ + - + - ]: 1 : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));
139 [ + - ]: 1 : const auto result = mempool_estimator.EstimateFeeRate(/*conservative=*/true);
140 [ + - + - : 2 : BOOST_CHECK(!result);
+ - ]
141 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result.error().reason, unloaded_err);
142 : 1 : }
143 [ + - ]: 1 : m_node.mempool->SetLoadTried(true);
144 : :
145 [ + - + - : 2 : BOOST_CHECK(!mempool_estimator.IsMempoolHealthy());
+ - + - ]
146 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.GetMempoolHealth() == MemPoolFeeRateEstimator::MempoolHealth::INSUFFICIENT_DATA);
+ - + - ]
147 : 1 : {
148 [ + - ]: 1 : const auto result = mempool_estimator.EstimateFeeRate(/*conservative=*/true);
149 : 1 : const std::string insufficient_err{strprintf("%s: Not enough recent block data for fee rate estimation",
150 [ + - + - ]: 1 : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY))};
151 [ + - + - : 2 : BOOST_CHECK(!result);
+ - ]
152 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result.error().reason, insufficient_err);
153 : 1 : }
154 : 1 : {
155 : 1 : MemPoolFeeRateEstimator custom_mempool_estimator{
156 [ + - + - ]: 1 : MempoolPolicyEstimatorPath(*m_node.args), *m_node.mempool, *m_node.chainman};
157 : 1 : unsigned int custom_height{100};
158 [ + + ]: 6 : for (size_t block_count{1}; block_count < MEMPOOL_HEALTH_WINDOW_BLOCKS; ++block_count) {
159 [ + - ]: 5 : AddRemovedBlock(custom_mempool_estimator,
160 : : /*removed_txs_weight=*/0,
161 : : /*block_txs_weight=*/0,
162 : : custom_height);
163 [ + - + - : 10 : BOOST_CHECK(!custom_mempool_estimator.IsMempoolHealthy());
+ - ]
164 : : }
165 : 1 : {
166 : 1 : const int64_t low_activity_weight{1000};
167 [ + - ]: 1 : AddRemovedBlock(custom_mempool_estimator, low_activity_weight / 2, low_activity_weight, custom_height);
168 : : }
169 : : // Below one block worth of total activity across the full window, even
170 : : // poor coverage in the only non-empty block is too noisy to reject the
171 : : // mempool as unhealthy.
172 [ + - + - : 2 : BOOST_CHECK(custom_mempool_estimator.IsMempoolHealthy());
+ - ]
173 : 1 : }
174 : 1 : size_t block_count = 1;
175 : 1 : const int64_t weight{DEFAULT_BLOCK_MAX_WEIGHT / 2};
176 : 1 : unsigned int height = 100;
177 : : // Equal weight
178 [ + + ]: 7 : while (block_count <= MEMPOOL_HEALTH_WINDOW_BLOCKS) {
179 [ + - ]: 6 : AddRemovedBlock(mempool_estimator, weight, weight, height);
180 [ + + ]: 6 : if (block_count < MEMPOOL_HEALTH_WINDOW_BLOCKS) {
181 [ + - + - : 10 : BOOST_CHECK(!mempool_estimator.IsMempoolHealthy());
+ - ]
182 : : }
183 : 6 : block_count += 1;
184 : : }
185 : : // Total txs weight ~11999k WU (~3.0 blocks), removed txs ~11999k WU (~3.0 blocks); coverage = 100%.
186 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.IsMempoolHealthy());
+ - + - ]
187 : : // Adding a single underrepresented block will not make the mempool unhealthy
188 : : // while the window coverage remains above the threshold.
189 [ + - ]: 1 : AddRemovedBlock(mempool_estimator, weight / 2, weight, height);
190 : : // Total txs weight ~11999k WU (~3.0 blocks), removed txs ~10999k WU (~2.75 blocks); coverage = ~92%.
191 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.IsMempoolHealthy());
+ - + - ]
192 : : // Empty block
193 : : // Total txs weight ~9999k WU (~2.5 blocks), removed txs ~8999k WU (~2.25 blocks); coverage = 90%.
194 [ + - ]: 1 : AddRemovedBlock(mempool_estimator, 0, 0, height);
195 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.IsMempoolHealthy());
+ - + - ]
196 : : // Total txs weight ~9999k WU (~2.5 blocks), removed txs ~7999k WU (~2.0 blocks); coverage = 80%.
197 [ + - ]: 1 : AddRemovedBlock(mempool_estimator, weight / 2, weight, height);
198 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.IsMempoolHealthy());
+ - + - ]
199 : : // Total txs weight ~9999k WU (~2.5 blocks), removed txs ~7000k WU (~1.75 blocks); coverage = 70%.
200 [ + - ]: 1 : AddRemovedBlock(mempool_estimator, weight / 2, weight, height);
201 [ + - + - : 2 : BOOST_CHECK(!mempool_estimator.IsMempoolHealthy());
+ - ]
202 : 1 : block_count = 1;
203 [ + + ]: 4 : while (block_count <= 3) {
204 [ + - ]: 3 : AddRemovedBlock(mempool_estimator, weight, weight, height);
205 [ + + ]: 3 : if (block_count < 3) {
206 [ + - + - : 4 : BOOST_CHECK(!mempool_estimator.IsMempoolHealthy());
+ - ]
207 : : }
208 : 3 : block_count += 1;
209 : : }
210 : : // Total txs weight ~9999k WU (~2.5 blocks), removed txs ~7999k WU (~2.0 blocks); coverage = 80%.
211 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.IsMempoolHealthy());
+ - + - ]
212 : :
213 : : // Reorg out and replace the last block. Replacing the tip block should keep a full
214 : : // healthy window when the replacement block has good mempool representation.
215 : 1 : height -= 1;
216 [ + - ]: 1 : AddRemovedBlock(mempool_estimator, weight, weight, height);
217 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.IsMempoolHealthy());
+ - + - ]
218 : :
219 : : // Reorg out the last two blocks. The estimator should discard the stale suffix,
220 : : // become temporarily unhealthy due to having fewer than MEMPOOL_HEALTH_WINDOW_BLOCKS stats,
221 : : // then recover after the replacement chain catches up.
222 : 1 : height -= 2;
223 [ + - ]: 1 : AddRemovedBlock(mempool_estimator, weight, weight, height);
224 [ + - + - : 2 : BOOST_CHECK(!mempool_estimator.IsMempoolHealthy());
+ - + - ]
225 [ + - ]: 1 : AddRemovedBlock(mempool_estimator, weight, weight, height);
226 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.IsMempoolHealthy());
+ - + - ]
227 : :
228 : : // A forward height gap (e.g. stale persisted stats after an unclean shutdown
229 : : // while the chain advanced) resets the tracked window entirely; the estimator
230 : : // stays unhealthy until a full window of contiguous blocks is seen again.
231 : 1 : height += 3;
232 [ + - ]: 1 : AddRemovedBlock(mempool_estimator, weight, weight, height);
233 [ + - + - : 2 : BOOST_CHECK(!mempool_estimator.IsMempoolHealthy());
+ - ]
234 [ + + ]: 6 : for (size_t i = 1; i < MEMPOOL_HEALTH_WINDOW_BLOCKS; ++i) {
235 [ + - ]: 5 : AddRemovedBlock(mempool_estimator, weight, weight, height);
236 [ + + ]: 5 : if (i < MEMPOOL_HEALTH_WINDOW_BLOCKS - 1) {
237 [ + - + - : 8 : BOOST_CHECK(!mempool_estimator.IsMempoolHealthy());
+ - ]
238 : : }
239 : : }
240 [ + - + - : 2 : BOOST_CHECK(mempool_estimator.IsMempoolHealthy());
+ - + - ]
241 : 1 : {
242 [ + - ]: 1 : LOCK(m_node.mempool->cs);
243 [ + - + - : 1 : BOOST_CHECK_EQUAL(m_node.mempool->GetTotalTxSize(), 0);
+ - ]
244 : 0 : }
245 : : // With an empty mempool there is nothing to build a feerate estimate from, so both
246 : : // estimates fall back to the floor fee rate: the higher of the minimum relay fee rate
247 : : // and the current mempool minimum fee rate.
248 [ + - + - ]: 1 : const FeePerVSize floor{std::max(m_node.mempool->m_opts.min_relay_feerate, m_node.mempool->GetMinFee()).GetFeePerVSize()};
249 : 1 : {
250 [ + - ]: 1 : const auto result = mempool_estimator.EstimateFeeRate(/*conservative=*/true);
251 [ + - + - : 2 : BOOST_REQUIRE(result.has_value());
+ - ]
252 [ + - + - : 3 : BOOST_CHECK(result->feerate == floor);
+ - + - ]
253 [ + - + - : 2 : BOOST_CHECK(result->feerate_estimator == FeeRateEstimatorType::MEMPOOL_POLICY);
+ - ]
254 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->returned_target, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET);
255 : :
256 : : // The floor estimate is cached like any other; a second call returns the same value.
257 [ + - ]: 1 : const auto cached_result = mempool_estimator.EstimateFeeRate(/*conservative=*/true);
258 [ + - + - : 2 : BOOST_REQUIRE(cached_result.has_value());
+ - ]
259 [ + - + - : 3 : BOOST_CHECK(cached_result->feerate == floor);
+ - ]
260 : 1 : }
261 : 1 : TestMemPoolEntryHelper entry;
262 [ + - + - : 1 : const auto tx_vsize = entry.FromTx(MakeRandomTx()).GetTxSize();
+ - + - ]
263 : 1 : const CAmount low_fee{CENT / 3000};
264 : 1 : const CAmount med_fee{CENT / 100};
265 : 1 : const CAmount high_fee{CENT / 10};
266 : 1 : const CAmount very_high_fee{CENT};
267 : : // A mempool that cannot fill 50% of a block leaves both percentiles empty,
268 : : // so both estimate still fall back to the floor.
269 : 1 : {
270 : : // Add high_fee transactions until mempool weight exceeds 25% of DEFAULT_BLOCK_MAX_WEIGHT.
271 : 1 : {
272 [ + - + - ]: 1 : LOCK2(cs_main, m_node.mempool->cs);
273 [ + + ]: 4034 : while ((m_node.mempool->GetTotalTxSize() * WITNESS_SCALE_FACTOR) <= (DEFAULT_BLOCK_MAX_WEIGHT * 25 / 100)) {
274 [ + - + - : 8066 : TryAddToMempool(*m_node.mempool, entry.Fee(high_fee).FromTx(MakeRandomTx()));
+ - + - ]
275 : : }
276 [ + - ]: 1 : }
277 : : // Expire the cached floor estimate so the denser mempool is observed.
278 [ + - ]: 1 : SetMockTime(GetTime<std::chrono::seconds>() + CACHE_LIFE + std::chrono::seconds{1});
279 [ + - ]: 1 : const auto result = mempool_estimator.EstimateFeeRate(/*conservative=*/true);
280 [ + - + - : 2 : BOOST_REQUIRE(result.has_value());
+ - ]
281 [ + - + - : 3 : BOOST_CHECK(result->feerate == floor);
+ - ]
282 : 0 : }
283 : : // A mempool that fills 50% of a block but not 75% has a conservative (p50)
284 : : // estimate, while the economical (p75) estimate falls back to the floor.
285 : 1 : {
286 : : // Add med_fee transactions until mempool weight exceeds 50% of DEFAULT_BLOCK_MAX_WEIGHT.
287 : 1 : {
288 [ + - + - ]: 1 : LOCK2(cs_main, m_node.mempool->cs);
289 [ + + ]: 4033 : while ((m_node.mempool->GetTotalTxSize() * WITNESS_SCALE_FACTOR) <= (DEFAULT_BLOCK_MAX_WEIGHT * 50 / 100)) {
290 [ + - + - : 8064 : TryAddToMempool(*m_node.mempool, entry.Fee(med_fee).FromTx(MakeRandomTx()));
+ - + - ]
291 : : }
292 [ + - ]: 1 : }
293 [ + - ]: 1 : SetMockTime(GetTime<std::chrono::seconds>() + CACHE_LIFE + std::chrono::seconds{1});
294 [ + - ]: 1 : const auto conservative = mempool_estimator.EstimateFeeRate(/*conservative=*/true);
295 [ + - ]: 1 : const auto economical = mempool_estimator.EstimateFeeRate(/*conservative=*/false);
296 [ + - + - : 2 : BOOST_REQUIRE(conservative.has_value());
+ - ]
297 [ + - + - : 2 : BOOST_REQUIRE(economical.has_value());
+ - ]
298 [ + - + - : 3 : BOOST_CHECK(conservative->feerate == FeeFrac(med_fee, tx_vsize));
+ - + - ]
299 [ + - + - : 3 : BOOST_CHECK(economical->feerate == floor);
+ - ]
300 : 1 : }
301 : : // Mempool transactions are enough to provide both feerate estimates.
302 : 1 : {
303 : : // Add low_fee transactions until mempool transactions weight
304 : : // is enough to reach the 75% coverage requirement
305 : 1 : {
306 [ + - + - ]: 1 : LOCK2(cs_main, m_node.mempool->cs);
307 [ + + ]: 4033 : while ((m_node.mempool->GetTotalTxSize() * WITNESS_SCALE_FACTOR) <= (DEFAULT_BLOCK_MAX_WEIGHT * 75 / 100)) {
308 [ + - + - : 8064 : TryAddToMempool(*m_node.mempool, entry.Fee(low_fee).FromTx(MakeRandomTx()));
+ - + - ]
309 : : }
310 [ + - ]: 1 : }
311 : : // Expire the sparse-result cache before expecting the estimator to observe the denser mempool.
312 [ + - ]: 1 : SetMockTime(GetTime<std::chrono::seconds>() + CACHE_LIFE + std::chrono::seconds{1});
313 [ + - ]: 1 : const auto result_conservative = mempool_estimator.EstimateFeeRate(/*conservative=*/true);
314 [ + - ]: 1 : const auto result_economical = mempool_estimator.EstimateFeeRate(/*conservative=*/false);
315 [ + - + - : 2 : BOOST_CHECK(result_conservative.has_value());
+ - ]
316 [ + - + - : 2 : BOOST_CHECK(result_economical.has_value());
+ - ]
317 [ + - + - : 3 : BOOST_CHECK(result_economical->feerate == FeeFrac(low_fee, tx_vsize));
+ - + - ]
318 [ + - + - : 3 : BOOST_CHECK(result_conservative->feerate == FeeFrac(med_fee, tx_vsize));
+ - + - ]
319 [ + - + - : 2 : BOOST_CHECK(ByRatio{result_conservative->feerate} > ByRatio{result_economical->feerate});
+ - ]
320 [ + - + - : 2 : BOOST_CHECK(result_conservative->feerate_estimator == FeeRateEstimatorType::MEMPOOL_POLICY);
+ - ]
321 [ + - + - : 2 : BOOST_CHECK(result_economical->feerate_estimator == FeeRateEstimatorType::MEMPOOL_POLICY);
+ - ]
322 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_conservative->returned_target, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET);
323 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_economical->returned_target, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET);
324 : :
325 : : // Adding another 30% of very-high-fee transactions should change the
326 : : // estimates after recomputation, but not while the cached estimate is fresh.
327 : 1 : {
328 [ + - + - ]: 1 : LOCK2(cs_main, m_node.mempool->cs);
329 [ + + ]: 4840 : while ((m_node.mempool->GetTotalTxSize() * WITNESS_SCALE_FACTOR) <=
330 : : (DEFAULT_BLOCK_MAX_WEIGHT * 105 / 100)) {
331 [ + - + - : 9678 : TryAddToMempool(*m_node.mempool, entry.Fee(very_high_fee).FromTx(MakeRandomTx()));
+ - + - ]
332 : : }
333 [ + - ]: 1 : }
334 [ + - + - : 4 : BOOST_CHECK(mempool_estimator.EstimateFeeRate(/*conservative=*/false).value().feerate == FeeFrac(low_fee, tx_vsize));
+ - + - +
- ]
335 [ + - + - : 4 : BOOST_CHECK(mempool_estimator.EstimateFeeRate(/*conservative=*/true).value().feerate == FeeFrac(med_fee, tx_vsize));
+ - + - ]
336 : : // Expire the cache by advancing mock time past CACHE_LIFE so the next call recomputes.
337 [ + - ]: 1 : SetMockTime(GetTime<std::chrono::seconds>() + CACHE_LIFE + std::chrono::seconds{1});
338 [ + - + - : 4 : BOOST_CHECK(mempool_estimator.EstimateFeeRate(/*conservative=*/false).value().feerate == FeeFrac(med_fee, tx_vsize));
+ - + - +
- ]
339 [ + - + - : 4 : BOOST_CHECK(mempool_estimator.EstimateFeeRate(/*conservative=*/true).value().feerate == FeeFrac(high_fee, tx_vsize));
+ - + - ]
340 : 1 : }
341 : 1 : }
342 : :
343 : : BOOST_AUTO_TEST_SUITE_END()
|