@ -539,6 +539,56 @@ static bool IsCurrentForFeeEstimation()
return true ;
return true ;
}
}
/* Make mempool consistent after a reorg, by re-adding or recursively erasing
* disconnected block transactions from the mempool , and also removing any
* other transactions from the mempool that are no longer valid given the new
* tip / height .
*
* Note : we assume that disconnectpool only contains transactions that are NOT
* confirmed in the current chain nor already in the mempool ( otherwise ,
* in - mempool descendants of such transactions would be removed ) .
*
* Passing fAddToMempool = false will skip trying to add the transactions back ,
* and instead just erase from the mempool as needed .
*/
void UpdateMempoolForReorg ( DisconnectedBlockTransactions & disconnectpool , bool fAddToMempool )
{
AssertLockHeld ( cs_main ) ;
std : : vector < uint256 > vHashUpdate ;
// disconnectpool's insertion_order index sorts the entries from
// oldest to newest, but the oldest entry will be the last tx from the
// latest mined block that was disconnected.
// Iterate disconnectpool in reverse, so that we add transactions
// back to the mempool starting with the earliest transaction that had
// been previously seen in a block.
auto it = disconnectpool . queuedTx . get < insertion_order > ( ) . rbegin ( ) ;
while ( it ! = disconnectpool . queuedTx . get < insertion_order > ( ) . rend ( ) ) {
// ignore validation errors in resurrected transactions
CValidationState stateDummy ;
if ( ! fAddToMempool | | ( * it ) - > IsCoinBase ( ) | | ! AcceptToMemoryPool ( mempool , stateDummy , * it , false , NULL , NULL , true ) ) {
// If the transaction doesn't make it in to the mempool, remove any
// transactions that depend on it (which would now be orphans).
mempool . removeRecursive ( * * it , MemPoolRemovalReason : : REORG ) ;
} else if ( mempool . exists ( ( * it ) - > GetHash ( ) ) ) {
vHashUpdate . push_back ( ( * it ) - > GetHash ( ) ) ;
}
+ + it ;
}
disconnectpool . queuedTx . clear ( ) ;
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
// no in-mempool children, which is generally not true when adding
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in
// the disconnectpool that were added back and cleans up the mempool state.
mempool . UpdateTransactionsFromBlock ( vHashUpdate ) ;
// We also need to remove any now-immature transactions
mempool . removeForReorg ( pcoinsTip , chainActive . Tip ( ) - > nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS ) ;
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize ( mempool , GetArg ( " -maxmempool " , DEFAULT_MAX_MEMPOOL_SIZE ) * 1000000 , GetArg ( " -mempoolexpiry " , DEFAULT_MEMPOOL_EXPIRY ) * 60 * 60 ) ;
}
bool AcceptToMemoryPoolWorker ( CTxMemPool & pool , CValidationState & state , const CTransactionRef & ptx , bool fLimitFree ,
bool AcceptToMemoryPoolWorker ( CTxMemPool & pool , CValidationState & state , const CTransactionRef & ptx , bool fLimitFree ,
bool * pfMissingInputs , int64_t nAcceptTime , std : : list < CTransactionRef > * plTxnReplaced ,
bool * pfMissingInputs , int64_t nAcceptTime , std : : list < CTransactionRef > * plTxnReplaced ,
bool fOverrideMempoolLimit , const CAmount & nAbsurdFee , std : : vector < uint256 > & vHashTxnToUncache )
bool fOverrideMempoolLimit , const CAmount & nAbsurdFee , std : : vector < uint256 > & vHashTxnToUncache )
@ -2119,8 +2169,17 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
}
}
/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */
/** Disconnect chainActive's tip.
bool static DisconnectTip ( CValidationState & state , const CChainParams & chainparams , bool fBare = false )
* After calling , the mempool will be in an inconsistent state , with
* transactions from disconnected blocks being added to disconnectpool . You
* should make the mempool consistent again by calling UpdateMempoolForReorg .
* with cs_main held .
*
* If disconnectpool is NULL , then no disconnected transactions are added to
* disconnectpool ( note that the caller is responsible for mempool consistency
* in any case ) .
*/
bool static DisconnectTip ( CValidationState & state , const CChainParams & chainparams , DisconnectedBlockTransactions * disconnectpool )
{
{
CBlockIndex * pindexDelete = chainActive . Tip ( ) ;
CBlockIndex * pindexDelete = chainActive . Tip ( ) ;
assert ( pindexDelete ) ;
assert ( pindexDelete ) ;
@ -2143,25 +2202,17 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
if ( ! FlushStateToDisk ( state , FLUSH_STATE_IF_NEEDED ) )
if ( ! FlushStateToDisk ( state , FLUSH_STATE_IF_NEEDED ) )
return false ;
return false ;
if ( ! fBare ) {
if ( disconnectpool ) {
// Resurrect mempool transactions from the disconnected block.
// Save transactions to re-add to mempool at end of reorg
std : : vector < uint256 > vHashUpdate ;
for ( auto it = block . vtx . rbegin ( ) ; it ! = block . vtx . rend ( ) ; + + it ) {
for ( const auto & it : block . vtx ) {
disconnectpool - > addTransaction ( * it ) ;
const CTransaction & tx = * it ;
// ignore validation errors in resurrected transactions
CValidationState stateDummy ;
if ( tx . IsCoinBase ( ) | | ! AcceptToMemoryPool ( mempool , stateDummy , it , false , NULL , NULL , true ) ) {
mempool . removeRecursive ( tx , MemPoolRemovalReason : : REORG ) ;
} else if ( mempool . exists ( tx . GetHash ( ) ) ) {
vHashUpdate . push_back ( tx . GetHash ( ) ) ;
}
}
while ( disconnectpool - > DynamicMemoryUsage ( ) > MAX_DISCONNECTED_TX_POOL_SIZE * 1000 ) {
// Drop the earliest entry, and remove its children from the mempool.
auto it = disconnectpool - > queuedTx . get < insertion_order > ( ) . begin ( ) ;
mempool . removeRecursive ( * * it , MemPoolRemovalReason : : REORG ) ;
disconnectpool - > removeEntry ( it ) ;
}
}
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
// no in-mempool children, which is generally not true when adding
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in this
// block that were added back and cleans up the mempool state.
mempool . UpdateTransactionsFromBlock ( vHashUpdate ) ;
}
}
// Update chainActive and related variables.
// Update chainActive and related variables.
@ -2249,7 +2300,7 @@ public:
*
*
* The block is added to connectTrace if connection succeeds .
* The block is added to connectTrace if connection succeeds .
*/
*/
bool static ConnectTip ( CValidationState & state , const CChainParams & chainparams , CBlockIndex * pindexNew , const std : : shared_ptr < const CBlock > & pblock , ConnectTrace & connectTrace )
bool static ConnectTip ( CValidationState & state , const CChainParams & chainparams , CBlockIndex * pindexNew , const std : : shared_ptr < const CBlock > & pblock , ConnectTrace & connectTrace , DisconnectedBlockTransactions & disconnectpool )
{
{
assert ( pindexNew - > pprev = = chainActive . Tip ( ) ) ;
assert ( pindexNew - > pprev = = chainActive . Tip ( ) ) ;
// Read block from disk.
// Read block from disk.
@ -2291,6 +2342,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
LogPrint ( BCLog : : BENCH , " - Writing chainstate: %.2fms [%.2fs] \n " , ( nTime5 - nTime4 ) * 0.001 , nTimeChainState * 0.000001 ) ;
LogPrint ( BCLog : : BENCH , " - Writing chainstate: %.2fms [%.2fs] \n " , ( nTime5 - nTime4 ) * 0.001 , nTimeChainState * 0.000001 ) ;
// Remove conflicting transactions from the mempool.;
// Remove conflicting transactions from the mempool.;
mempool . removeForBlock ( blockConnecting . vtx , pindexNew - > nHeight ) ;
mempool . removeForBlock ( blockConnecting . vtx , pindexNew - > nHeight ) ;
disconnectpool . removeForBlock ( blockConnecting . vtx ) ;
// Update chainActive & related variables.
// Update chainActive & related variables.
UpdateTip ( pindexNew , chainparams ) ;
UpdateTip ( pindexNew , chainparams ) ;
@ -2384,9 +2436,14 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
// Disconnect active blocks which are no longer in the best chain.
// Disconnect active blocks which are no longer in the best chain.
bool fBlocksDisconnected = false ;
bool fBlocksDisconnected = false ;
DisconnectedBlockTransactions disconnectpool ;
while ( chainActive . Tip ( ) & & chainActive . Tip ( ) ! = pindexFork ) {
while ( chainActive . Tip ( ) & & chainActive . Tip ( ) ! = pindexFork ) {
if ( ! DisconnectTip ( state , chainparams ) )
if ( ! DisconnectTip ( state , chainparams , & disconnectpool ) ) {
// This is likely a fatal error, but keep the mempool consistent,
// just in case. Only remove from the mempool in this case.
UpdateMempoolForReorg ( disconnectpool , false ) ;
return false ;
return false ;
}
fBlocksDisconnected = true ;
fBlocksDisconnected = true ;
}
}
@ -2409,7 +2466,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
// Connect new blocks.
// Connect new blocks.
BOOST_REVERSE_FOREACH ( CBlockIndex * pindexConnect , vpindexToConnect ) {
BOOST_REVERSE_FOREACH ( CBlockIndex * pindexConnect , vpindexToConnect ) {
if ( ! ConnectTip ( state , chainparams , pindexConnect , pindexConnect = = pindexMostWork ? pblock : std : : shared_ptr < const CBlock > ( ) , connectTrace ) ) {
if ( ! ConnectTip ( state , chainparams , pindexConnect , pindexConnect = = pindexMostWork ? pblock : std : : shared_ptr < const CBlock > ( ) , connectTrace , disconnectpool ) ) {
if ( state . IsInvalid ( ) ) {
if ( state . IsInvalid ( ) ) {
// The block violates a consensus rule.
// The block violates a consensus rule.
if ( ! state . CorruptionPossible ( ) )
if ( ! state . CorruptionPossible ( ) )
@ -2420,6 +2477,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
break ;
break ;
} else {
} else {
// A system error occurred (disk space, database error, ...).
// A system error occurred (disk space, database error, ...).
// Make the mempool consistent with the current tip, just in case
// any observers try to use it before shutdown.
UpdateMempoolForReorg ( disconnectpool , false ) ;
return false ;
return false ;
}
}
} else {
} else {
@ -2434,8 +2494,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
}
}
if ( fBlocksDisconnected ) {
if ( fBlocksDisconnected ) {
mempool . removeForReorg ( pcoinsTip , chainActive . Tip ( ) - > nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS ) ;
// If any blocks were disconnected, disconnectpool may be non empty. Add
LimitMempoolSize ( mempool , GetArg ( " -maxmempool " , DEFAULT_MAX_MEMPOOL_SIZE ) * 1000000 , GetArg ( " -mempoolexpiry " , DEFAULT_MEMPOOL_EXPIRY ) * 60 * 60 ) ;
// any disconnected transactions back to the mempool.
UpdateMempoolForReorg ( disconnectpool , true ) ;
}
}
mempool . check ( pcoinsTip ) ;
mempool . check ( pcoinsTip ) ;
@ -2584,6 +2645,7 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
setDirtyBlockIndex . insert ( pindex ) ;
setDirtyBlockIndex . insert ( pindex ) ;
setBlockIndexCandidates . erase ( pindex ) ;
setBlockIndexCandidates . erase ( pindex ) ;
DisconnectedBlockTransactions disconnectpool ;
while ( chainActive . Contains ( pindex ) ) {
while ( chainActive . Contains ( pindex ) ) {
CBlockIndex * pindexWalk = chainActive . Tip ( ) ;
CBlockIndex * pindexWalk = chainActive . Tip ( ) ;
pindexWalk - > nStatus | = BLOCK_FAILED_CHILD ;
pindexWalk - > nStatus | = BLOCK_FAILED_CHILD ;
@ -2591,13 +2653,17 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
setBlockIndexCandidates . erase ( pindexWalk ) ;
setBlockIndexCandidates . erase ( pindexWalk ) ;
// ActivateBestChain considers blocks already in chainActive
// ActivateBestChain considers blocks already in chainActive
// unconditionally valid already, so force disconnect away from it.
// unconditionally valid already, so force disconnect away from it.
if ( ! DisconnectTip ( state , chainparams ) ) {
if ( ! DisconnectTip ( state , chainparams , & disconnectpool ) ) {
mempool . removeForReorg ( pcoinsTip , chainActive . Tip ( ) - > nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS ) ;
// It's probably hopeless to try to make the mempool consistent
// here if DisconnectTip failed, but we can try.
UpdateMempoolForReorg ( disconnectpool , false ) ;
return false ;
return false ;
}
}
}
}
LimitMempoolSize ( mempool , GetArg ( " -maxmempool " , DEFAULT_MAX_MEMPOOL_SIZE ) * 1000000 , GetArg ( " -mempoolexpiry " , DEFAULT_MEMPOOL_EXPIRY ) * 60 * 60 ) ;
// DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
UpdateMempoolForReorg ( disconnectpool , true ) ;
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add it again.
// add it again.
@ -2610,7 +2676,6 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
}
}
InvalidChainFound ( pindex ) ;
InvalidChainFound ( pindex ) ;
mempool . removeForReorg ( pcoinsTip , chainActive . Tip ( ) - > nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS ) ;
uiInterface . NotifyBlockTip ( IsInitialBlockDownload ( ) , pindex - > pprev ) ;
uiInterface . NotifyBlockTip ( IsInitialBlockDownload ( ) , pindex - > pprev ) ;
return true ;
return true ;
}
}
@ -3724,7 +3789,7 @@ bool RewindBlockIndex(const CChainParams& params)
// of the blockchain).
// of the blockchain).
break ;
break ;
}
}
if ( ! DisconnectTip ( state , params , true ) ) {
if ( ! DisconnectTip ( state , params , NULL ) ) {
return error ( " RewindBlockIndex: unable to disconnect block at height %i " , pindex - > nHeight ) ;
return error ( " RewindBlockIndex: unable to disconnect block at height %i " , pindex - > nHeight ) ;
}
}
// Occasionally flush state to disk.
// Occasionally flush state to disk.