Skip to content

Commit a97a892

Browse files
committed
Merge bitcoin#28368: Fee Estimator updates from Validation Interface/CScheduler thread
91504cb rpc: `SyncWithValidationInterfaceQueue` on fee estimation RPC's (ismaelsadeeq) 7145239 tx fees, policy: CBlockPolicyEstimator update from `CValidationInterface` notifications (ismaelsadeeq) dff5ad3 CValidationInterface: modify the parameter of `TransactionAddedToMempool` (ismaelsadeeq) 91532bd tx fees, policy: update `CBlockPolicyEstimator::processBlock` parameter (ismaelsadeeq) bfcd401 CValidationInterface, mempool: add new callback to `CValidationInterface` (ismaelsadeeq) 0889e07 tx fees, policy: cast with static_cast instead of C-Style cast (ismaelsadeeq) a0e3eb7 tx fees, policy: bugfix: move `removeTx` into reason != `BLOCK` condition (ismaelsadeeq) Pull request description: This is an attempt to bitcoin#11775 This Pr will enable fee estimator to listen to ValidationInterface notifications to process new transactions added and removed from the mempool. This PR includes the following changes: - Added a new callback to the Validation Interface `MempoolTransactionsRemovedForConnectedBlock`, which notifies listeners about the transactions that have been removed due to a new block being connected, along with the height at which the transactions were removed. - Modified the `TransactionAddedToMempool` callback parameter to include additional information about the transaction needed for fee estimation. - Updated `CBlockPolicyEstimator` to process transactions using` CTransactionRef` instead of `CTxMempoolEntry.` - Implemented the `CValidationInterface` interface in `CBlockPolicyEstimater` and overridden the `TransactionAddedToMempool`, `TransactionRemovedFromMempool`, and `MempoolTransactionsRemovedForConnectedBlock` methods to receive updates from their notifications. Prior to this PR, the fee estimator updates from the mempool, i.e whenever a new block is connected all transactions in the block that are in our mempool are going to be removed using the `removeForBlock` function in `txmempool.cpp`. This removal triggered updates to the fee estimator. As a result, the fee estimator would block mempool's `cs` until it finished updating every time a new block was connected. Instead of being blocked only on mempool tx removal, we were blocking on both tx removal and fee estimator updating. If we want to further improve fee estimation, or add heavy-calulation steps to it, it is currently not viable as we would be slowing down block relay in the process This PR is smaller in terms of the changes made compared to bitcoin#11775, as it focuses solely on enabling fee estimator updates from the validationInterface/cscheduler thread notifications. I have not split the validation interface because, as I understand it, the rationale behind the split in bitcoin#11775 was to have `MempoolInterface` signals come from the mempool and `CValidationInterface` events come from validation. I believe this separation can be achieved in a separate refactoring PR when the need arises. Also left out some commits from bitcoin#11775 - Some refactoring which are no longer needed. - Handle reorgs much better in fee estimator. - Track witness hash malleation in fee estimator I believe they are a separate change that can come in a follow-up after this. ACKs for top commit: achow101: ACK 91504cb TheCharlatan: Re-ACK 91504cb willcl-ark: ACK 91504cb Tree-SHA512: 846dfb9da57a8a42458827b8975722d153907fe6302ad65748d74f311e1925557ad951c3d95fe71fb90ddcc8a3710c45abb343ab86b88780871cb9c38c72c7b1
2 parents 18bed14 + 91504cb commit a97a892

20 files changed

+307
-123
lines changed

src/Makefile.am

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,6 @@ libbitcoinkernel_la_SOURCES = \
954954
node/chainstate.cpp \
955955
node/utxo_snapshot.cpp \
956956
policy/feerate.cpp \
957-
policy/fees.cpp \
958957
policy/packages.cpp \
959958
policy/policy.cpp \
960959
policy/rbf.cpp \

src/init.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,12 @@ void Shutdown(NodeContext& node)
284284
DumpMempool(*node.mempool, MempoolPath(*node.args));
285285
}
286286

287-
// Drop transactions we were still watching, and record fee estimations.
288-
if (node.fee_estimator) node.fee_estimator->Flush();
287+
// Drop transactions we were still watching, record fee estimations and Unregister
288+
// fee estimator from validation interface.
289+
if (node.fee_estimator) {
290+
node.fee_estimator->Flush();
291+
UnregisterValidationInterface(node.fee_estimator.get());
292+
}
289293

290294
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
291295
if (node.chainman) {
@@ -1239,6 +1243,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
12391243
// Flush estimates to disk periodically
12401244
CBlockPolicyEstimator* fee_estimator = node.fee_estimator.get();
12411245
node.scheduler->scheduleEvery([fee_estimator] { fee_estimator->FlushFeeEstimates(); }, FEE_FLUSH_INTERVAL);
1246+
RegisterValidationInterface(fee_estimator);
12421247
}
12431248

12441249
// Check port numbers
@@ -1452,7 +1457,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14521457
assert(!node.chainman);
14531458

14541459
CTxMemPool::Options mempool_opts{
1455-
.estimator = node.fee_estimator.get(),
14561460
.check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0,
14571461
};
14581462
auto result{ApplyArgsManOptions(args, chainparams, mempool_opts)};

src/kernel/mempool_entry.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,63 @@ class CTxMemPoolEntry
190190

191191
using CTxMemPoolEntryRef = CTxMemPoolEntry::CTxMemPoolEntryRef;
192192

193+
struct TransactionInfo {
194+
const CTransactionRef m_tx;
195+
/* The fee the transaction paid */
196+
const CAmount m_fee;
197+
/**
198+
* The virtual transaction size.
199+
*
200+
* This is a policy field which considers the sigop cost of the
201+
* transaction as well as its weight, and reinterprets it as bytes.
202+
*
203+
* It is the primary metric by which the mining algorithm selects
204+
* transactions.
205+
*/
206+
const int64_t m_virtual_transaction_size;
207+
/* The block height the transaction entered the mempool */
208+
const unsigned int txHeight;
209+
210+
TransactionInfo(const CTransactionRef& tx, const CAmount& fee, const int64_t vsize, const unsigned int height)
211+
: m_tx{tx},
212+
m_fee{fee},
213+
m_virtual_transaction_size{vsize},
214+
txHeight{height} {}
215+
};
216+
217+
struct RemovedMempoolTransactionInfo {
218+
TransactionInfo info;
219+
explicit RemovedMempoolTransactionInfo(const CTxMemPoolEntry& entry)
220+
: info{entry.GetSharedTx(), entry.GetFee(), entry.GetTxSize(), entry.GetHeight()} {}
221+
};
222+
223+
struct NewMempoolTransactionInfo {
224+
TransactionInfo info;
225+
/*
226+
* This boolean indicates whether the transaction was added
227+
* without enforcing mempool fee limits.
228+
*/
229+
const bool m_from_disconnected_block;
230+
/* This boolean indicates whether the transaction is part of a package. */
231+
const bool m_submitted_in_package;
232+
/*
233+
* This boolean indicates whether the blockchain is up to date when the
234+
* transaction is added to the mempool.
235+
*/
236+
const bool m_chainstate_is_current;
237+
/* Indicates whether the transaction has unconfirmed parents. */
238+
const bool m_has_no_mempool_parents;
239+
240+
explicit NewMempoolTransactionInfo(const CTransactionRef& tx, const CAmount& fee,
241+
const int64_t vsize, const unsigned int height,
242+
const bool from_disconnected_block, const bool submitted_in_package,
243+
const bool chainstate_is_current,
244+
const bool has_no_mempool_parents)
245+
: info{tx, fee, vsize, height},
246+
m_from_disconnected_block{from_disconnected_block},
247+
m_submitted_in_package{submitted_in_package},
248+
m_chainstate_is_current{chainstate_is_current},
249+
m_has_no_mempool_parents{has_no_mempool_parents} {}
250+
};
251+
193252
#endif // BITCOIN_KERNEL_MEMPOOL_ENTRY_H

src/kernel/mempool_options.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
#include <cstdint>
1414
#include <optional>
1515

16-
class CBlockPolicyEstimator;
17-
1816
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
1917
static constexpr unsigned int DEFAULT_MAX_MEMPOOL_SIZE_MB{300};
2018
/** Default for -maxmempool when blocksonly is set */
@@ -37,8 +35,6 @@ namespace kernel {
3735
* Most of the time, this struct should be referenced as CTxMemPool::Options.
3836
*/
3937
struct MemPoolOptions {
40-
/* Used to estimate appropriate transaction fees. */
41-
CBlockPolicyEstimator* estimator{nullptr};
4238
/* The ratio used to determine how often sanity checks will run. */
4339
int check_ratio{0};
4440
int64_t max_size_bytes{DEFAULT_MAX_MEMPOOL_SIZE_MB * 1'000'000};

src/node/interfaces.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,9 @@ class NotificationsProxy : public CValidationInterface
428428
explicit NotificationsProxy(std::shared_ptr<Chain::Notifications> notifications)
429429
: m_notifications(std::move(notifications)) {}
430430
virtual ~NotificationsProxy() = default;
431-
void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) override
431+
void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence) override
432432
{
433-
m_notifications->transactionAddedToMempool(tx);
433+
m_notifications->transactionAddedToMempool(tx.info.m_tx);
434434
}
435435
void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override
436436
{

src/policy/fees.cpp

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -515,15 +515,10 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
515515
}
516516
}
517517

518-
// This function is called from CTxMemPool::removeUnchecked to ensure
519-
// txs removed from the mempool for any reason are no longer
520-
// tracked. Txs that were part of a block have already been removed in
521-
// processBlockTx to ensure they are never double tracked, but it is
522-
// of no harm to try to remove them again.
523-
bool CBlockPolicyEstimator::removeTx(uint256 hash, bool inBlock)
518+
bool CBlockPolicyEstimator::removeTx(uint256 hash)
524519
{
525520
LOCK(m_cs_fee_estimator);
526-
return _removeTx(hash, inBlock);
521+
return _removeTx(hash, /*inBlock=*/false);
527522
}
528523

529524
bool CBlockPolicyEstimator::_removeTx(const uint256& hash, bool inBlock)
@@ -579,11 +574,26 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const fs::path& estimation_filepath
579574

580575
CBlockPolicyEstimator::~CBlockPolicyEstimator() = default;
581576

582-
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
577+
void CBlockPolicyEstimator::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /*unused*/)
578+
{
579+
processTransaction(tx);
580+
}
581+
582+
void CBlockPolicyEstimator::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason /*unused*/, uint64_t /*unused*/)
583+
{
584+
removeTx(tx->GetHash());
585+
}
586+
587+
void CBlockPolicyEstimator::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight)
588+
{
589+
processBlock(txs_removed_for_block, nBlockHeight);
590+
}
591+
592+
void CBlockPolicyEstimator::processTransaction(const NewMempoolTransactionInfo& tx)
583593
{
584594
LOCK(m_cs_fee_estimator);
585-
unsigned int txHeight = entry.GetHeight();
586-
uint256 hash = entry.GetTx().GetHash();
595+
const unsigned int txHeight = tx.info.txHeight;
596+
const auto& hash = tx.info.m_tx->GetHash();
587597
if (mapMemPoolTxs.count(hash)) {
588598
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n",
589599
hash.ToString());
@@ -597,39 +607,45 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
597607
// It will be synced next time a block is processed.
598608
return;
599609
}
610+
// This transaction should only count for fee estimation if:
611+
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
612+
// - the node is not behind
613+
// - the transaction is not dependent on any other transactions in the mempool
614+
// - it's not part of a package.
615+
const bool validForFeeEstimation = !tx.m_from_disconnected_block && !tx.m_submitted_in_package && tx.m_chainstate_is_current && tx.m_has_no_mempool_parents;
600616

601617
// Only want to be updating estimates when our blockchain is synced,
602618
// otherwise we'll miscalculate how many blocks its taking to get included.
603-
if (!validFeeEstimate) {
619+
if (!validForFeeEstimation) {
604620
untrackedTxs++;
605621
return;
606622
}
607623
trackedTxs++;
608624

609625
// Feerates are stored and reported as BTC-per-kb:
610-
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize());
626+
const CFeeRate feeRate(tx.info.m_fee, tx.info.m_virtual_transaction_size);
611627

612628
mapMemPoolTxs[hash].blockHeight = txHeight;
613-
unsigned int bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
629+
unsigned int bucketIndex = feeStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
614630
mapMemPoolTxs[hash].bucketIndex = bucketIndex;
615-
unsigned int bucketIndex2 = shortStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
631+
unsigned int bucketIndex2 = shortStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
616632
assert(bucketIndex == bucketIndex2);
617-
unsigned int bucketIndex3 = longStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
633+
unsigned int bucketIndex3 = longStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
618634
assert(bucketIndex == bucketIndex3);
619635
}
620636

621-
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry)
637+
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const RemovedMempoolTransactionInfo& tx)
622638
{
623639
AssertLockHeld(m_cs_fee_estimator);
624-
if (!_removeTx(entry->GetTx().GetHash(), true)) {
640+
if (!_removeTx(tx.info.m_tx->GetHash(), true)) {
625641
// This transaction wasn't being tracked for fee estimation
626642
return false;
627643
}
628644

629645
// How many blocks did it take for miners to include this transaction?
630646
// blocksToConfirm is 1-based, so a transaction included in the earliest
631647
// possible block has confirmation count of 1
632-
int blocksToConfirm = nBlockHeight - entry->GetHeight();
648+
int blocksToConfirm = nBlockHeight - tx.info.txHeight;
633649
if (blocksToConfirm <= 0) {
634650
// This can't happen because we don't process transactions from a block with a height
635651
// lower than our greatest seen height
@@ -638,16 +654,16 @@ bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM
638654
}
639655

640656
// Feerates are stored and reported as BTC-per-kb:
641-
CFeeRate feeRate(entry->GetFee(), entry->GetTxSize());
657+
CFeeRate feeRate(tx.info.m_fee, tx.info.m_virtual_transaction_size);
642658

643-
feeStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
644-
shortStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
645-
longStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
659+
feeStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
660+
shortStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
661+
longStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
646662
return true;
647663
}
648664

649-
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
650-
std::vector<const CTxMemPoolEntry*>& entries)
665+
void CBlockPolicyEstimator::processBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
666+
unsigned int nBlockHeight)
651667
{
652668
LOCK(m_cs_fee_estimator);
653669
if (nBlockHeight <= nBestSeenHeight) {
@@ -676,8 +692,8 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
676692

677693
unsigned int countedTxs = 0;
678694
// Update averages with data points from current block
679-
for (const auto& entry : entries) {
680-
if (processBlockTx(nBlockHeight, entry))
695+
for (const auto& tx : txs_removed_for_block) {
696+
if (processBlockTx(nBlockHeight, tx))
681697
countedTxs++;
682698
}
683699

@@ -688,7 +704,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
688704

689705

690706
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy estimates updated by %u of %u block txs, since last block %u of %u tracked, mempool map size %u, max target %u from %s\n",
691-
countedTxs, entries.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size(),
707+
countedTxs, txs_removed_for_block.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size(),
692708
MaxUsableEstimate(), HistoricalBlockSpan() > BlockSpan() ? "historical" : "current");
693709

694710
trackedTxs = 0;

src/policy/fees.h

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <threadsafety.h>
1313
#include <uint256.h>
1414
#include <util/fs.h>
15+
#include <validationinterface.h>
1516

1617
#include <array>
1718
#include <chrono>
@@ -35,8 +36,9 @@ static constexpr std::chrono::hours MAX_FILE_AGE{60};
3536
static constexpr bool DEFAULT_ACCEPT_STALE_FEE_ESTIMATES{false};
3637

3738
class AutoFile;
38-
class CTxMemPoolEntry;
3939
class TxConfirmStats;
40+
struct RemovedMempoolTransactionInfo;
41+
struct NewMempoolTransactionInfo;
4042

4143
/* Identifier for each of the 3 different TxConfirmStats which will track
4244
* history over different time horizons. */
@@ -143,7 +145,7 @@ struct FeeCalculation
143145
* a certain number of blocks. Every time a block is added to the best chain, this class records
144146
* stats on the transactions included in that block
145147
*/
146-
class CBlockPolicyEstimator
148+
class CBlockPolicyEstimator : public CValidationInterface
147149
{
148150
private:
149151
/** Track confirm delays up to 12 blocks for short horizon */
@@ -198,19 +200,19 @@ class CBlockPolicyEstimator
198200
public:
199201
/** Create new BlockPolicyEstimator and initialize stats tracking classes with default values */
200202
CBlockPolicyEstimator(const fs::path& estimation_filepath, const bool read_stale_estimates);
201-
~CBlockPolicyEstimator();
203+
virtual ~CBlockPolicyEstimator();
202204

203205
/** Process all the transactions that have been included in a block */
204-
void processBlock(unsigned int nBlockHeight,
205-
std::vector<const CTxMemPoolEntry*>& entries)
206+
void processBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
207+
unsigned int nBlockHeight)
206208
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
207209

208210
/** Process a transaction accepted to the mempool*/
209-
void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
211+
void processTransaction(const NewMempoolTransactionInfo& tx)
210212
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
211213

212-
/** Remove a transaction from the mempool tracking stats*/
213-
bool removeTx(uint256 hash, bool inBlock)
214+
/** Remove a transaction from the mempool tracking stats for non BLOCK removal reasons*/
215+
bool removeTx(uint256 hash)
214216
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
215217

216218
/** DEPRECATED. Return a feerate estimate */
@@ -260,6 +262,15 @@ class CBlockPolicyEstimator
260262
/** Calculates the age of the file, since last modified */
261263
std::chrono::hours GetFeeEstimatorFileAge();
262264

265+
protected:
266+
/** Overridden from CValidationInterface. */
267+
void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /*unused*/) override
268+
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
269+
void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason /*unused*/, uint64_t /*unused*/) override
270+
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
271+
void MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight) override
272+
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
273+
263274
private:
264275
mutable Mutex m_cs_fee_estimator;
265276

@@ -290,7 +301,7 @@ class CBlockPolicyEstimator
290301
std::map<double, unsigned int> bucketMap GUARDED_BY(m_cs_fee_estimator); // Map of bucket upper-bound to index into all vectors by bucket
291302

292303
/** Process a transaction confirmed in a block*/
293-
bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
304+
bool processBlockTx(unsigned int nBlockHeight, const RemovedMempoolTransactionInfo& tx) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
294305

295306
/** Helper for estimateSmartFee */
296307
double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);

src/rpc/fees.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <txmempool.h>
1515
#include <univalue.h>
1616
#include <util/fees.h>
17+
#include <validationinterface.h>
1718

1819
#include <algorithm>
1920
#include <array>
@@ -67,6 +68,7 @@ static RPCHelpMan estimatesmartfee()
6768
const NodeContext& node = EnsureAnyNodeContext(request.context);
6869
const CTxMemPool& mempool = EnsureMemPool(node);
6970

71+
SyncWithValidationInterfaceQueue();
7072
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
7173
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
7274
bool conservative = true;
@@ -155,6 +157,7 @@ static RPCHelpMan estimaterawfee()
155157
{
156158
CBlockPolicyEstimator& fee_estimator = EnsureAnyFeeEstimator(request.context);
157159

160+
SyncWithValidationInterfaceQueue();
158161
unsigned int max_target = fee_estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
159162
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
160163
double threshold = 0.95;

0 commit comments

Comments
 (0)