Improve re-insertion of TXs from disconnected blocks into mempool #917
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.
Closes #915
Should be ported to Handshake after review to close handshake-org/hsd#306
Current behavior
FullNode
, when a block is removed from the chain, the chain event'disconnect'
triggersmempool._removeBlock()
, so the mempool can reset its internaltip
, and also add back the unconfirmed transactions to the mempool. I'm not sure why, but if the mempool if emtpy at this time, the transaction re-insertions is totally skipped:bcoin/lib/mempool/mempool.js
Lines 246 to 250 in 3623d04
FullNode
, when chain emits a'reorganize'
event, that triggersmempool._handleReorg()
which should clear out all transactions in the mempool (and those re-inserted from disconnected blocks) that are no longer valid at the new chain height. Examples of TXs that might not be valid after a reorg include premature coinbase spends and premature sequence-lock spends. The current implementation is incomplete in that it does not check the context of any transactions in this process: if a TX simply has a coinbase spend or has a sequence lock, it is dropped.Updated behavior
mempool._removeBlock()
attempts to re-insert transactions from disconnected blocks no matter what the current state of the mempool is. Note that each TX goes through themempool.InsertTX()
function which verifies everything as if it were the first time we'd ever seen the TX.mempool._handleReorg()
now checks the context of each TX in the mempool, via thespentView
. If the TX spends from a coinbase, the maturity is checked. If the TX is version > 1, the sequence locks are checked. Transactions that are OK are allowed to remain in the mempool.Testing
For what it's worth, this code passes the Bitcoin Core testing framework BIP68 test which I've been playing with on another branch. I did not implement a CSV-by-time test since the logic would be the same as by-height, but could if we want that. I've also got an actual CSV-by-time test in an open PR #613
Recommendation
I didn't include it in this PR, but I think it's also important that we check how
'disconnect'
and'reorganize'
events are used throughout the codebase. For example,rpc invalidateblock
will NOT emit either event, leaving the mempool in a potentially invalid state. Both_removeBlock()
and_handleReorg()
should be called on the mempool every time a block is disconnected, for any reason. I have one such implementation here, but given that @braydonf is working on thechain-expansion
branch with so much chain refactoring, I just think we need to make sure that the mempool is handled correctly when we get there.