Skip to content

Commit 7145239

Browse files
tx fees, policy: CBlockPolicyEstimator update from CValidationInterface notifications
`CBlockPolicyEstimator` will implement `CValidationInterface` and subscribe to its notification to process transactions added and removed from the mempool. Re-delegate calculation of `validForFeeEstimation` from validation to fee estimator. Also clean up the validForFeeEstimation arg thats no longer needed in `CTxMempool`. Co-authored-by: Matt Corallo <git@bluematt.me>
1 parent dff5ad3 commit 7145239

File tree

13 files changed

+158
-74
lines changed

13 files changed

+158
-74
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
@@ -285,8 +285,12 @@ void Shutdown(NodeContext& node)
285285
DumpMempool(*node.mempool, MempoolPath(*node.args));
286286
}
287287

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

291295
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
292296
if (node.chainman) {
@@ -1258,6 +1262,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
12581262
// Flush estimates to disk periodically
12591263
CBlockPolicyEstimator* fee_estimator = node.fee_estimator.get();
12601264
node.scheduler->scheduleEvery([fee_estimator] { fee_estimator->FlushFeeEstimates(); }, FEE_FLUSH_INTERVAL);
1265+
RegisterValidationInterface(fee_estimator);
12611266
}
12621267

12631268
// Check port numbers
@@ -1471,7 +1476,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14711476
assert(!node.chainman);
14721477

14731478
CTxMemPool::Options mempool_opts{
1474-
.estimator = node.fee_estimator.get(),
14751479
.check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0,
14761480
};
14771481
auto result{ApplyArgsManOptions(args, chainparams, mempool_opts)};

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/policy/fees.cpp

Lines changed: 28 additions & 12 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,17 +607,23 @@ 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;
613629
unsigned int bucketIndex = feeStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));

src/policy/fees.h

Lines changed: 16 additions & 6 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,9 +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;
4040
struct RemovedMempoolTransactionInfo;
41+
struct NewMempoolTransactionInfo;
4142

4243
/* Identifier for each of the 3 different TxConfirmStats which will track
4344
* history over different time horizons. */
@@ -144,7 +145,7 @@ struct FeeCalculation
144145
* a certain number of blocks. Every time a block is added to the best chain, this class records
145146
* stats on the transactions included in that block
146147
*/
147-
class CBlockPolicyEstimator
148+
class CBlockPolicyEstimator : public CValidationInterface
148149
{
149150
private:
150151
/** Track confirm delays up to 12 blocks for short horizon */
@@ -199,19 +200,19 @@ class CBlockPolicyEstimator
199200
public:
200201
/** Create new BlockPolicyEstimator and initialize stats tracking classes with default values */
201202
CBlockPolicyEstimator(const fs::path& estimation_filepath, const bool read_stale_estimates);
202-
~CBlockPolicyEstimator();
203+
virtual ~CBlockPolicyEstimator();
203204

204205
/** Process all the transactions that have been included in a block */
205206
void processBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
206207
unsigned int nBlockHeight)
207208
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
208209

209210
/** Process a transaction accepted to the mempool*/
210-
void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
211+
void processTransaction(const NewMempoolTransactionInfo& tx)
211212
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
212213

213-
/** Remove a transaction from the mempool tracking stats*/
214-
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)
215216
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
216217

217218
/** DEPRECATED. Return a feerate estimate */
@@ -261,6 +262,15 @@ class CBlockPolicyEstimator
261262
/** Calculates the age of the file, since last modified */
262263
std::chrono::hours GetFeeEstimatorFileAge();
263264

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+
264274
private:
265275
mutable Mutex m_cs_fee_estimator;
266276

src/test/fuzz/package_eval.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
121121
mempool_opts.expiry = std::chrono::hours{fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)};
122122
nBytesPerSigOp = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(1, 999);
123123

124-
mempool_opts.estimator = nullptr;
125124
mempool_opts.check_ratio = 1;
126125
mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
127126

src/test/fuzz/policy_estimator.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,16 @@ FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
4444
return;
4545
}
4646
const CTransaction tx{*mtx};
47-
block_policy_estimator.processTransaction(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx), fuzzed_data_provider.ConsumeBool());
47+
const CTxMemPoolEntry& entry = ConsumeTxMemPoolEntry(fuzzed_data_provider, tx);
48+
const auto tx_info = NewMempoolTransactionInfo(entry.GetSharedTx(), entry.GetFee(),
49+
entry.GetTxSize(), entry.GetHeight(),
50+
/* m_from_disconnected_block */ false,
51+
/* m_submitted_in_package */ false,
52+
/* m_chainstate_is_current */ true,
53+
/* m_has_no_mempool_parents */ fuzzed_data_provider.ConsumeBool());
54+
block_policy_estimator.processTransaction(tx_info);
4855
if (fuzzed_data_provider.ConsumeBool()) {
49-
(void)block_policy_estimator.removeTx(tx.GetHash(), /*inBlock=*/fuzzed_data_provider.ConsumeBool());
56+
(void)block_policy_estimator.removeTx(tx.GetHash());
5057
}
5158
},
5259
[&] {
@@ -69,7 +76,7 @@ FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
6976
block_policy_estimator.processBlock(txs, fuzzed_data_provider.ConsumeIntegral<unsigned int>());
7077
},
7178
[&] {
72-
(void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider), /*inBlock=*/fuzzed_data_provider.ConsumeBool());
79+
(void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider));
7380
},
7481
[&] {
7582
block_policy_estimator.FlushUnconfirmed();

src/test/fuzz/tx_pool.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ CTxMemPool MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeConte
123123
CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
124124

125125
// ...override specific options for this specific fuzz suite
126-
mempool_opts.estimator = nullptr;
127126
mempool_opts.check_ratio = 1;
128127
mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
129128

0 commit comments

Comments
 (0)