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 <policy/fees/mempool_estimator.h>
6 : :
7 : : #include <logging.h>
8 : : #include <node/miner.h>
9 : : #include <policy/feerate.h>
10 : : #include <policy/policy.h>
11 : : #include <primitives/block.h>
12 : : #include <serialize.h>
13 : : #include <streams.h>
14 : : #include <sync.h>
15 : : #include <tinyformat.h>
16 : : #include <txmempool.h>
17 : : #include <util/check.h>
18 : : #include <util/feefrac.h>
19 : : #include <util/fees.h>
20 : : #include <util/fs.h>
21 : : #include <util/overflow.h>
22 : : #include <util/syserror.h>
23 : : #include <validation.h>
24 : :
25 : : #include <algorithm>
26 : : #include <iterator>
27 : : #include <numeric>
28 : : #include <optional>
29 : : #include <string>
30 : : #include <string_view>
31 : : #include <system_error>
32 : : #include <utility>
33 : :
34 : : constexpr int CURRENT_MEMPOOL_ESTIMATOR_VERSION{1};
35 : :
36 : : namespace {
37 : : struct MinedBlockStatsFormatter {
38 : : template <typename Stream>
39 : 3444 : void Ser(Stream& s, const MinedBlockStats& v)
40 : : {
41 : 3444 : s << v.m_height << v.m_removed_block_txs_weight << v.m_block_weight;
42 : 3444 : }
43 : : template <typename Stream>
44 : 1559 : void Unser(Stream& s, MinedBlockStats& v)
45 : : {
46 : 1559 : s >> v.m_height >> v.m_removed_block_txs_weight >> v.m_block_weight;
47 : 1559 : }
48 : : };
49 : :
50 : 103894 : void AddMinedBlockStats(std::vector<MinedBlockStats>& mined_blocks, MinedBlockStats stats)
51 : : {
52 : 103894 : const auto stale_begin{std::find_if(mined_blocks.begin(), mined_blocks.end(), [&](const MinedBlockStats& block) {
53 [ + + ]: 612116 : return block.m_height >= stats.m_height;
54 : : })};
55 [ + + ]: 103894 : const auto stale_count{std::distance(stale_begin, mined_blocks.end())};
56 [ + + ]: 103894 : if (stale_count > 0) {
57 [ + - ]: 286 : LogDebug(BCLog::ESTIMATEFEE,
58 : : "%s: connected block height=%s discards tracked mined-block stats "
59 : : "from height=%s to height=%s; stale_stats=%s",
60 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
61 : : stats.m_height,
62 : : stale_begin->m_height,
63 : : mined_blocks.back().m_height,
64 : : stale_count);
65 : : }
66 : 103894 : mined_blocks.erase(stale_begin, mined_blocks.end());
67 [ + + + + ]: 103894 : if (!mined_blocks.empty() && mined_blocks.back().m_height + 1 != stats.m_height) {
68 [ + - - + ]: 7 : LogDebug(BCLog::ESTIMATEFEE,
69 : : "%s: clearing mined-block stats after height gap; tracked_stats=%s "
70 : : "expected_height=%s received_height=%s",
71 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
72 : : mined_blocks.size(),
73 : : mined_blocks.back().m_height + 1,
74 : : stats.m_height);
75 [ + - ]: 7 : mined_blocks.clear();
76 : : }
77 : :
78 [ - + + + ]: 103894 : if (mined_blocks.size() == MEMPOOL_HEALTH_WINDOW_BLOCKS) mined_blocks.erase(mined_blocks.begin());
79 : 103894 : mined_blocks.push_back(stats);
80 : 103894 : }
81 : :
82 : : struct ActiveTip {
83 : : int height;
84 : : uint256 hash;
85 : : };
86 : :
87 : 418 : std::optional<ActiveTip> GetActiveTip(const ChainstateManager& chainman)
88 : : {
89 : 418 : LOCK(::cs_main);
90 [ + - ]: 418 : const CBlockIndex* tip{chainman.ActiveTip()};
91 [ + + ]: 418 : if (!tip) return std::nullopt;
92 : 400 : return ActiveTip{tip->nHeight, tip->GetBlockHash()};
93 : 418 : }
94 : : } // namespace
95 : :
96 : 465 : MemPoolFeeRateEstimator::Percentiles MemPoolFeeRateEstimator::CalculateMaxWeightPercentiles(std::span<const FeePerVSize> chunk_feerates)
97 : : {
98 [ - + ]: 41246 : Assume(std::is_sorted(chunk_feerates.begin(), chunk_feerates.end(), [](const auto& a, const auto& b) { return ByRatio{a} > ByRatio{b}; }));
99 : 465 : constexpr int64_t total_weight{DEFAULT_BLOCK_MAX_WEIGHT};
100 : 465 : const int64_t p50_weight{total_weight / 2};
101 : 465 : const int64_t p75_weight{total_weight * 3 / 4};
102 : 465 : Percentiles percentiles{};
103 : 465 : int64_t accumulated_weight{0};
104 [ + + ]: 37270 : for (const auto& curr_feerate : chunk_feerates) {
105 : 36811 : accumulated_weight += int64_t{curr_feerate.size} * WITNESS_SCALE_FACTOR;
106 [ + + + + ]: 36811 : if (accumulated_weight >= p50_weight && percentiles.p50.IsEmpty()) {
107 : 9 : percentiles.p50 = curr_feerate;
108 : : }
109 [ + + ]: 36811 : if (accumulated_weight >= p75_weight && percentiles.p75.IsEmpty()) {
110 : 6 : percentiles.p75 = curr_feerate;
111 : 6 : break;
112 : : }
113 : : }
114 : 465 : return percentiles;
115 : : }
116 : :
117 : 3173 : bool MemPoolFeeRateEstimatorCache::IsStale() const
118 : : {
119 [ + + + + ]: 3173 : return !m_fee_rate_estimation || (m_last_updated + CACHE_LIFE) < NodeClock::now();
120 : : }
121 : :
122 : : std::optional<MemPoolFeeRateEstimatorCache::FeeRateEstimate>
123 : 3170 : MemPoolFeeRateEstimatorCache::GetCachedEstimate(const uint256& tip_hash) const
124 : : {
125 [ + + + + ]: 3170 : if (IsStale() || tip_hash != m_tip_hash) return std::nullopt;
126 : 2706 : return m_fee_rate_estimation;
127 : : }
128 : :
129 : 462 : void MemPoolFeeRateEstimatorCache::Update(FeePerVSize conservative, FeePerVSize economical, const uint256& tip_hash)
130 : : {
131 [ + + ]: 462 : m_fee_rate_estimation = {conservative, economical};
132 : 462 : m_tip_hash = tip_hash;
133 : 462 : m_last_updated = NodeClock::now();
134 : 462 : }
135 : :
136 : 104486 : void MemPoolFeeRateEstimatorCache::Clear()
137 : : {
138 [ + + ]: 104486 : m_fee_rate_estimation.reset();
139 : 104486 : m_tip_hash.SetNull();
140 : 104486 : m_last_updated = {};
141 : 104486 : }
142 : :
143 : : //! Build the error result for a failed mempool fee rate estimation.
144 : 15 : static util::Unexpected<FeeRateEstimationError> EstimationError(std::string error)
145 : : {
146 : 15 : return EstimationError(FeeRateEstimatorType::MEMPOOL_POLICY, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET, std::move(error));
147 : : }
148 : :
149 : 3180 : static std::optional<std::string_view> MempoolHealthError(MemPoolFeeRateEstimator::MempoolHealth health)
150 : : {
151 [ + + + - ]: 3180 : switch (health) {
152 : 11 : case MemPoolFeeRateEstimator::MempoolHealth::INSUFFICIENT_DATA:
153 : 11 : return "Not enough recent block data for fee rate estimation";
154 : 3 : case MemPoolFeeRateEstimator::MempoolHealth::LOW_COVERAGE:
155 : 3 : return "Mempool is unreliable for fee rate estimation";
156 : 3166 : case MemPoolFeeRateEstimator::MempoolHealth::HEALTHY:
157 : 3166 : return std::nullopt;
158 : : }
159 : 0 : Assume(false);
160 : 0 : return std::nullopt;
161 : : }
162 : :
163 : 1147 : MemPoolFeeRateEstimator::MemPoolFeeRateEstimator(fs::path mempool_estimator_file_path,
164 : : const CTxMemPool& mempool,
165 : 1147 : ChainstateManager& chainman)
166 : 1147 : : m_mempool(mempool),
167 : 1147 : m_chainman(chainman),
168 : 1147 : m_mempool_estimator_file_path(std::move(mempool_estimator_file_path))
169 : : {
170 [ + - ]: 1147 : ReadFromDisk();
171 : 1147 : }
172 : :
173 : 1147 : void MemPoolFeeRateEstimator::ReadFromDisk()
174 : : {
175 : 2294 : AutoFile file{fsbridge::fopen(m_mempool_estimator_file_path, "rb")};
176 [ + + ]: 1147 : if (file.IsNull()) {
177 [ + - + - : 1068 : LogDebug(BCLog::ESTIMATEFEE, "%s: %s does not exist. Continuing anyway",
- + + - +
- ]
178 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
179 : : fs::PathToString(m_mempool_estimator_file_path));
180 : 534 : return;
181 : : }
182 [ + - + + ]: 613 : if (Read(file)) {
183 [ + - + + : 1181 : LogDebug(BCLog::ESTIMATEFEE, "%s: mined-block stats successfully read from %s.",
- + + - +
- ]
184 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
185 : : fs::PathToString(m_mempool_estimator_file_path));
186 : : }
187 : 1147 : }
188 : :
189 : 613 : bool MemPoolFeeRateEstimator::Read(AutoFile& file)
190 : : {
191 : 613 : try {
192 : 613 : int version_required;
193 [ + - ]: 613 : file >> version_required;
194 [ - + ]: 613 : if (version_required != CURRENT_MEMPOOL_ESTIMATOR_VERSION) {
195 [ - - - - ]: 613 : LogWarning("%s: file version not supported; continuing anyway",
196 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));
197 : : return false;
198 : : }
199 : : // Stage into a local buffer and commit to the member only after validation passes.
200 : 613 : std::vector<MinedBlockStats> blocks;
201 [ + - ]: 613 : file >> Using<VectorFormatter<MinedBlockStatsFormatter>>(blocks);
202 : 613 : uint256 tip_hash;
203 [ + - ]: 613 : file >> tip_hash;
204 [ - + - + ]: 613 : if (blocks.size() > MEMPOOL_HEALTH_WINDOW_BLOCKS) {
205 [ # # # # ]: 0 : LogWarning("%s: Number of previously mined blocks read exceeds the maximum of %s; ignoring file",
206 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
207 : : MEMPOOL_HEALTH_WINDOW_BLOCKS);
208 : : return false;
209 : : }
210 [ + + ]: 1754 : for (size_t i = 1; i < blocks.size(); ++i) {
211 [ - + ]: 1141 : const uint64_t expected_height{SaturatingAdd(blocks[i - 1].m_height, uint64_t{1})};
212 [ - + ]: 1141 : if (blocks[i].m_height != expected_height) {
213 [ - - - - ]: 21 : LogWarning("%s: Non-consecutive block heights read, expected height %s but found %s; ignoring file",
214 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
215 : : expected_height, blocks[i].m_height);
216 : : return false;
217 : : }
218 : : }
219 [ + + ]: 613 : if (!blocks.empty()) {
220 : 418 : const auto& last_block{blocks.back()};
221 [ + - ]: 418 : const std::optional<ActiveTip> active_tip{GetActiveTip(m_chainman)};
222 [ + + ]: 418 : if (!active_tip) {
223 [ + - + - : 18 : LogWarning("%s: Mined-block stats read end at height %s block %s, but there is no active chain tip; ignoring file",
+ - ]
224 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
225 : : last_block.m_height, tip_hash.ToString());
226 : 18 : return false;
227 : : }
228 [ + + + + ]: 400 : if (last_block.m_height != static_cast<uint64_t>(active_tip->height) || tip_hash != active_tip->hash) {
229 [ + - + - : 3 : LogWarning("%s: Mined-block stats read end at height %s block %s, but the active chain tip is height %s block %s; ignoring file",
+ - + - ]
230 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
231 : : last_block.m_height, tip_hash.ToString(),
232 : : active_tip->height, active_tip->hash.ToString());
233 : 3 : return false;
234 : : }
235 : : }
236 [ + - ]: 592 : LOCK(cs);
237 : 592 : m_prev_mined_blocks = std::move(blocks);
238 : 592 : m_mined_blocks_tip_hash = tip_hash;
239 [ + - ]: 592 : m_cache.Clear();
240 [ - - ]: 613 : } catch (const std::exception&) {
241 [ - - - - ]: 0 : LogWarning("%s: Unable to read mined-block stats from stream (non-fatal)",
242 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));
243 : 0 : return false;
244 : 0 : }
245 : 592 : return true;
246 : : }
247 : :
248 : 1149 : bool MemPoolFeeRateEstimator::Write(AutoFile& file) const
249 : : {
250 : 1149 : try {
251 [ + - ]: 1149 : LOCK(cs);
252 [ + - ]: 1149 : file << CURRENT_MEMPOOL_ESTIMATOR_VERSION;
253 [ + - ]: 1149 : file << Using<VectorFormatter<MinedBlockStatsFormatter>>(m_prev_mined_blocks);
254 [ + - + - ]: 2298 : file << m_mined_blocks_tip_hash;
255 [ - - ]: 0 : } catch (const std::exception&) {
256 : 0 : return false;
257 : 0 : }
258 : 1149 : return true;
259 : : }
260 : :
261 : 1149 : void MemPoolFeeRateEstimator::FlushMinedBlockStats()
262 : : {
263 [ + - ]: 2298 : if (!m_mempool_estimator_file_path.parent_path().empty()) {
264 : 1149 : std::error_code error;
265 [ + - ]: 1149 : fs::create_directories(m_mempool_estimator_file_path.parent_path(), error);
266 [ - + ]: 1149 : if (error) {
267 [ # # # # : 0 : LogWarning("%s: failed to create mempool policy estimator directory %s: %s. Continuing anyway",
# # # # ]
268 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
269 : : fs::PathToString(m_mempool_estimator_file_path.parent_path()), error.message());
270 : 0 : return;
271 : : }
272 : : }
273 : 2298 : AutoFile file{fsbridge::fopen(m_mempool_estimator_file_path, "wb")};
274 [ - + ]: 1149 : if (file.IsNull()) {
275 [ # # # # : 0 : LogWarning("%s: unable to open %s for writing. Continuing anyway",
# # ]
276 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
277 : : fs::PathToString(m_mempool_estimator_file_path));
278 : 0 : return;
279 : : }
280 [ + - - + ]: 1149 : if (!Write(file)) {
281 [ # # # # : 0 : LogWarning("%s: Unable to write mined-block stats to %s (non-fatal)",
# # ]
282 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
283 : : fs::PathToString(m_mempool_estimator_file_path));
284 : : }
285 [ + - - + ]: 2298 : if (file.fclose() != 0) {
286 [ # # # # : 0 : LogWarning("Failed to close mempool policy estimator file %s: %s. Continuing anyway.",
# # ]
287 : : fs::PathToString(m_mempool_estimator_file_path), SysErrorString(errno));
288 : 0 : return;
289 : : }
290 [ + - + + : 2295 : LogDebug(BCLog::ESTIMATEFEE, "%s: mined-block stats flushed to %s.",
- + + - +
- ]
291 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY),
292 : : fs::PathToString(m_mempool_estimator_file_path));
293 : 1149 : }
294 : :
295 : 607 : void MemPoolFeeRateEstimator::MempoolLoadFailed()
296 : : {
297 : 607 : LOCK(cs);
298 [ + + + - ]: 607 : if (m_prev_mined_blocks.empty()) return;
299 [ + - + - : 42 : LogDebug(BCLog::ESTIMATEFEE, "%s: mempool did not load; clearing mined-block stats",
+ - + - ]
300 : : FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY));
301 [ + - + - ]: 84 : m_prev_mined_blocks.clear();
302 : 607 : }
303 : :
304 : 103894 : void MemPoolFeeRateEstimator::MempoolTxsRemovedForBlock(const std::shared_ptr<const CBlock>& block,
305 : : const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
306 : : unsigned int block_height)
307 : : {
308 : 103894 : LOCK(cs);
309 [ - + ]: 103894 : Assert(!block->vtx.empty());
310 : : // Accumulate total block weight and removed mempool tx weight, both excluding the coinbase.
311 : 503108 : const auto get_tx_weight = [](const CTransactionRef& tx) {
312 : 798428 : return static_cast<uint64_t>(GetTransactionWeight(*tx));
313 : : };
314 : : // Skip vtx[0], which is the coinbase.
315 : 103894 : const uint64_t block_weight = std::accumulate(std::next(block->vtx.begin()), block->vtx.end(), uint64_t{0},
316 : 217415 : [&](uint64_t acc, const CTransactionRef& tx) {
317 : 217415 : return acc + get_tx_weight(tx);
318 : : });
319 : 103894 : const uint64_t removed_weight = std::accumulate(
320 : : txs_removed_for_block.begin(), txs_removed_for_block.end(), uint64_t{0},
321 : 181799 : [&](uint64_t acc, const RemovedMempoolTransactionInfo& tx) {
322 : 181799 : return acc + get_tx_weight(tx.info.m_tx);
323 : : });
324 [ + - ]: 103894 : AddMinedBlockStats(m_prev_mined_blocks, {block_height, removed_weight, block_weight});
325 [ + - ]: 103894 : m_mined_blocks_tip_hash = block->GetHash();
326 [ + - ]: 103894 : m_cache.Clear();
327 : 103894 : }
328 : :
329 : : // Require at least one block worth of activity across the window before using
330 : : // the coverage ratio as a representative mempool health signal.
331 : : static constexpr uint64_t MIN_REPRESENTATIVE_WINDOW_WEIGHT{DEFAULT_BLOCK_MAX_WEIGHT};
332 : :
333 : 3210 : MemPoolFeeRateEstimator::MempoolHealth MemPoolFeeRateEstimator::GetMempoolHealth() const
334 : : {
335 : 3210 : LOCK(cs);
336 [ + - ]: 3210 : const auto estimator_name{FeeRateEstimatorTypeToString(FeeRateEstimatorType::MEMPOOL_POLICY)};
337 [ - + + + ]: 3210 : if (m_prev_mined_blocks.size() < MEMPOOL_HEALTH_WINDOW_BLOCKS) {
338 [ + - + - : 29 : LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check failed; tracked_blocks=%s required_blocks=%s",
- + + - ]
339 : : estimator_name, m_prev_mined_blocks.size(), MEMPOOL_HEALTH_WINDOW_BLOCKS);
340 : 29 : return MempoolHealth::INSUFFICIENT_DATA;
341 : : }
342 : 3181 : uint64_t total_block_weight{0};
343 : 3181 : uint64_t total_removed_weight{0};
344 : 3181 : uint64_t expected_height{m_prev_mined_blocks.front().m_height};
345 [ + + ]: 22267 : for (const auto& block : m_prev_mined_blocks) {
346 : 19086 : Assume(block.m_height == expected_height);
347 : 19086 : ++expected_height;
348 : 19086 : total_block_weight += block.m_block_weight;
349 : 19086 : total_removed_weight += block.m_removed_block_txs_weight;
350 : : }
351 : : // Too little block activity for the coverage ratio to be meaningful; skip it.
352 [ + + ]: 3181 : if (total_block_weight < MIN_REPRESENTATIVE_WINDOW_WEIGHT) {
353 [ + - + - : 3153 : LogDebug(BCLog::ESTIMATEFEE, "%s: mempool health check passed; low activity, total_block_weight=%s minimum=%s",
+ - ]
354 : : estimator_name, total_block_weight, MIN_REPRESENTATIVE_WINDOW_WEIGHT);
355 : 3153 : return MempoolHealth::HEALTHY;
356 : : }
357 : 28 : const double representation_ratio = static_cast<double>(total_removed_weight) / total_block_weight;
358 [ + - + - : 34 : LogDebug(BCLog::ESTIMATEFEE,
+ + + - ]
359 : : "%s: mempool health check %s; removed_weight=%s total_block_weight=%s "
360 : : "coverage=%.2f required_coverage=%.2f",
361 : : estimator_name,
362 : : representation_ratio >= MEMPOOL_REPRESENTATION_THRESHOLD ? "passed" : "failed",
363 : : total_removed_weight,
364 : : total_block_weight,
365 : : representation_ratio,
366 : : MEMPOOL_REPRESENTATION_THRESHOLD);
367 [ + + ]: 28 : return representation_ratio >= MEMPOOL_REPRESENTATION_THRESHOLD ? MempoolHealth::HEALTHY : MempoolHealth::LOW_COVERAGE;
368 : 3210 : }
369 : :
370 : 3181 : util::Expected<FeeRateEstimation, FeeRateEstimationError> MemPoolFeeRateEstimator::EstimateFeeRate(bool conservative) const
371 : : {
372 : 3181 : constexpr auto estimator_type{FeeRateEstimatorType::MEMPOOL_POLICY};
373 [ + + ]: 3181 : if (!m_mempool.GetLoadTried()) {
374 : 1 : return EstimationError(strprintf("%s: Mempool not loaded yet, no fee rate estimate available", FeeRateEstimatorTypeToString(estimator_type)));
375 : : }
376 [ + + ]: 3180 : if (auto error{MempoolHealthError(GetMempoolHealth())}) {
377 : 14 : return EstimationError(strprintf("%s: %s", FeeRateEstimatorTypeToString(estimator_type), *error));
378 : : }
379 : : // The estimator lock is not held while building a block template, so
380 : : // in a rare edge case concurrent callers may duplicate work.
381 : : //
382 : : // Cached fee rate estimates are tagged with the chain tip they were computed on
383 : : // and only served from the cache while that tip is current.
384 : : //
385 : : // The fee rate estimate returned directly below may still reflect a tip that went
386 : : // stale during the call; that is an accepted tradeoff of not holding
387 : : // locks across block assembly.
388 : 3166 : {
389 [ - + + - ]: 9498 : const uint256 tip_hash{WITH_LOCK(::cs_main, return Assume(m_chainman.CurrentChainstate().m_chain.Tip())->GetBlockHash())};
390 : 3166 : LOCK(cs);
391 [ + - ]: 3166 : const auto cached_estimate = m_cache.GetCachedEstimate(tip_hash);
392 [ + + ]: 3166 : if (cached_estimate) {
393 : 2705 : const auto cached_feerate{
394 [ + + ]: 2705 : conservative ? cached_estimate->m_conservative : cached_estimate->m_economical};
395 [ + - ]: 2705 : return FeeRateEstimation{estimator_type, cached_feerate, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET};
396 : : }
397 : 2705 : }
398 : 461 : node::BlockCreateOptions options;
399 : 461 : options.test_block_validity = false;
400 [ + - + - : 1844 : const auto blocktemplate = WITH_LOCK(::cs_main, return (node::BlockAssembler{m_chainman.CurrentChainstate(), &m_mempool, options}).CreateNewBlock());
+ - + - ]
401 [ - + - - : 461 : if (!blocktemplate) return EstimationError(strprintf("%s: Failed to create block template for fee rate estimation", FeeRateEstimatorTypeToString(estimator_type)));
- - ]
402 : : // Sort again because the rounding up when converting from weight to vsize may cause slight misorder.
403 [ - - - - : 449926 : std::sort(blocktemplate->m_package_feerates.begin(), blocktemplate->m_package_feerates.end(), [](const auto& a, const auto& b) { return ByRatio{a} > ByRatio{b}; });
+ + + + +
+ + + + +
+ + + + -
+ - - +
+ ]
404 [ - + + - ]: 461 : const auto percentiles = CalculateMaxWeightPercentiles(blocktemplate->m_package_feerates);
405 : : // Fall back to a relayable floor (the higher of the min relay fee and the current
406 : : // mempool min fee) for any percentile the mempool was too sparse to fill.
407 [ + - + + ]: 461 : const FeePerVSize floor{std::max(m_mempool.m_opts.min_relay_feerate, m_mempool.GetMinFee()).GetFeePerVSize()};
408 [ + + ]: 461 : const FeePerVSize p50{percentiles.p50.IsEmpty() ? floor : percentiles.p50};
409 [ + + ]: 461 : const FeePerVSize p75{percentiles.p75.IsEmpty() ? floor : percentiles.p75};
410 [ + - + - ]: 1383 : WITH_LOCK(cs, m_cache.Update(p50, p75, blocktemplate->block.hashPrevBlock));
411 [ + - + - : 1383 : LogDebug(BCLog::ESTIMATEFEE, "%s: conservative/economical fee rate: %s/%s %s/kvB",
+ - + - +
- + - ]
412 : : FeeRateEstimatorTypeToString(estimator_type), CFeeRate(p50).GetFeePerK(),
413 : : CFeeRate(p75).GetFeePerK(), CURRENCY_ATOM);
414 [ + + ]: 918 : return FeeRateEstimation{estimator_type, conservative ? p50 : p75, MEMPOOL_FEE_ESTIMATOR_MAX_TARGET};
415 : 461 : }
|