improve mempool reorg logic when the peak is a non-transaction block #17370
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Purpose:
This PR addresses the issue raised by #17186.
Background
In order for the mempool to evict transactions as they become invalid (either because they made it into a block or the coins they spend became spent by some other transaction), it needs to know about new blocks added to the chain.
The "ground truth" about whether a transaction is valid or not is the
CoinStore
, which the mempool has access to via theget_coin_record
function, passed into it. This function reflects the peak of the chain, the current coin set.transaction blocks
The mempool needs to keep a reference to the most recent transaction block, because those are what matters for timelock conditions. e.g. height-based timelock conditions compare the argument against the previous transaction block's height, and the time-based timelock conditions compare the argument against the previous transaction block's timestamp. Only transaction blocks have timestamps. (see mempool_check_time_locks())
Since the mempool validates transactions before including them, it needs to know about the most recent transaction block, not necessarily the actual peak.
Reorgs
When a re-org happens, the fork we switch over to is not replayed one block at a time to the mempool, it will just learn about the most recent peak. If this peak is not a transaction block, there's not much the mempool can do about it (currently). It can't update its set of transactions, because it doesn't know what the most recent transcaction block is.
As pointed out in #17186, this delays updating the mempool until we receive a transaction block on the new chain. This in turn risks us failing to create a valid block if we happen to find a valid proof-of-space for that next transaction block.
Solution
Since the mempool itself can't traverse the blockchain, even if it would detect the reorg, it can't step back to find the most recent transaction block on the new chain. A simpler solution (I believe) is to ensure that the full node only ever passes the most recent transaction block to the mempool.
This patch adds a sibling function to
Blockchain.get_peak()
calledBlockchain.get_tx_peak()
, which returns the most recent transaction block (which may be the peak, if that is a transaction block).Current Behavior:
the mempool may be delayed one block in updating after a reorg
New Behavior:
The mempool updates immediately with a reorg
Testing Notes:
There are two new tests for the
Blockchain
class, one where blocks are just added linearly and theget_tx_block()
is tested to always return the most recent tx block. The other test exrcises a reorg where the switch-over happens on a non-transaction block, and ensures it still returns the most recent one.