Motivation
Now that we have the full linearization of mempool clusters, whenever a cluster in the mempool gets updated either due to replacement, eviction for various reasons, or block connection, we now have the previous linearization of the affected cluster and the new linearization of the new clusters available.
This brings two advantages the ability for outside observers to make decisions based on fee rate diagram updates without direct access to the mempool, and reduced lock contention.
It can used primarily for:
- Making
CBlockPolicyEstimatorpackage aware - Preventing redundant block template builds in the proposed block template manager BlockTemplateManager Proposal.
During the development of cluster mempool those usage was discussed. Note: This is not the motivation behind cluster mempool just a part of it.
See discussion in Package aware fee estimator and Determining BlockTemplate Fee Increase Using Fee Rate Diagram.
Currently the fee estimator is asynchronous but not package-aware. After this PR it is possible to make it package-aware, a follow-up branch on top of this does exactly that see commit.
For the mining interface, the node every 1 seconds rebuilds a block template regardless of whether any significant mempool change occurred to the top block.
Even after cluster mempool, building a block template is not free since we have to pause the node (holding cs_main).
With this notification, a proposed block template manager that observes the mempool update will only rebuild after an inflow in the mempool top block template warrants it. Redundant builds where no significant fee rate change occurred are eliminated.
And doing this becomes even more useful because of proposed improvements like mempool-based fee rate estimation, which also builds a block template. Right now, it uses a constant 7-second interval and rebuilds regardless of whether an inflow affects the top block template or not. This will fix that.
A proposed sendtemplate in the p2p layer also builds a block template. I propose that it also use the proposed block template manager, which should be built on top of this, so that redundant block template builds are eliminated.
There is a different approach to determining updates in the top block template, but that still requires some direct interaction with the mempool. Since the fee estimator needs this notification anyway and we already have it, the block template manager can use it too and not interact with the mempool at all, it becomes a purely passive observer.
Changes
Added
TxGraph::Chunk, a new struct carrying a chunk fee rate plus the transactionTxGraph::Refs in that chunk. This lets before/after fee-rate diagrams carry enough information to build mempool chunk notifications.Added
MemPoolChunk(kernel/mempool_entry.h), representing a mempool chunk with:m_fee_rate— chunk fee rate.m_transactions— transactions in the chunk.m_sigops_cost— total sigops cost for the chunk.m_chunk_hash— deterministic hash identifying the chunk transaction set, computed before dispatching the validation callback.
Added
MemPoolChunksUpdate(kernel/mempool_entry.h), carrying:old_chunks— chunks before the update.new_chunks— chunks after the update.reason—MemPoolRemovalReasonidentifying the update type.block_height— set only for block-connected removals; defaults tostd::nullopt.
Added a new
CValidationInterface::MempoolUpdatedcallback receiving aMemPoolChunksUpdate.Added
GetHashFromWitnesses(src/util/hasher.{h,cpp}), which sorts a vector ofWtxids deterministically and hashes them to produce a stable chunk hash.GetPackageHashinsrc/policy/packages.cppnow uses this helper.Changed
TxGraph::GetMainStagingDiagrams()to return chunk objects instead of fee-rate-only diagrams.Cached the fee-rate diagram chunks inside
CTxMemPool::ChangeSet, so the same snapshot can be reused for RBF feerate checks and later notification emission without recomputing a different diagram.Extracted the dependency addition logic in
UpdateTransactionsFromBlockinto the private helperaddDependenciesFromBlock.
MempoolUpdated is emitted for mempool update paths including:
- Transaction addition and RBF replacement: emitted in
CTxMemPool::ChangeSet::Applywith reasonREPLACED. - Block connection: emitted in
removeForBlockwith reasonBLOCK;block_heightcarries the connecting block height. - Reorg removals: emitted in
removeForReorgwith reasonREORG. - Recursive removals: emitted in
removeRecursivewith the caller-supplied reason, such asCONFLICT,EXPIRY, orREORG. - Expiry: emitted in
Expirewith reasonEXPIRY. - Size-limit eviction: emitted in
TrimToSizewith reasonSIZELIMIT. - Post-reorg dependency update: emitted in
UpdateTransactionsFromBlockwith reasonSIZELIMIT.
The before/after chunks are snapshotted from the txgraph staging state before committing the staging graph. The mempool entries are removed afterward
through RemoveStaged.
Note on UpdateTransactionsFromBlock:
A naive single-pass approach that adds dependencies and trims directly can leave the staging graph oversized, and oversized clusters cannot produce a valid fee-rate diagram. The implementation therefore uses a two-stage approach:
Phase 1: create txgraph staging, call
addDependenciesFromBlockto register newly discovered parent-child relationships, then callTrim().If
Trim()returns no transactions, no cluster exceeded the limit. The code snapshots the diagram, commits staging, emitsMempoolUpdated(SIZELIMIT), and finishes.If
Trim()returns transactions to evict, the oversized staging graph is discarded. A fresh staging graph is opened, the evicted txs are removed from txgraph staging, dependencies are added again, the diagram is snapshotted, staging is committed,MempoolUpdated(SIZELIMIT)is emitted, and thenRemoveStagedfully removes the evicted entries from the mempool.
Reorgs are rare, and reorgs that exceed the cluster size limit are rarer still, so the common optimistic path only applies dependencies once.
Added mempool_update_tests, a unit test suite covering the MempoolUpdated emission paths and validating chunk contents, fee rates, block height
propagation, and deterministic chunk hashes.
Chunk hashes are computed in ValidationSignals::MempoolUpdated before the event is queued, so subscribers receive chunks with populated hashes while the
mempool mutation path avoids carrying hash computation logic directly.