@ -658,6 +658,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
CBlockIndex * pindex = ( * mi ) . second ;
if ( chain . Contains ( pindex ) )
return pindex ;
if ( pindex - > GetAncestor ( chain . Height ( ) ) = = chain . Tip ( ) ) {
return chain . Tip ( ) ;
}
}
}
return chain . Genesis ( ) ;
@ -2777,7 +2780,7 @@ 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. */
bool static DisconnectTip ( CValidationState & state , const CChainParams & chainparams )
bool static DisconnectTip ( CValidationState & state , const CChainParams & chainparams , bool fBare = false )
{
CBlockIndex * pindexDelete = chainActive . Tip ( ) ;
assert ( pindexDelete ) ;
@ -2797,24 +2800,28 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
// Write the chain state to disk, if necessary.
if ( ! FlushStateToDisk ( state , FLUSH_STATE_IF_NEEDED ) )
return false ;
// Resurrect mempool transactions from the disconnected block.
std : : vector < uint256 > vHashUpdate ;
BOOST_FOREACH ( const CTransaction & tx , block . vtx ) {
// ignore validation errors in resurrected transactions
list < CTransaction > removed ;
CValidationState stateDummy ;
if ( tx . IsCoinBase ( ) | | ! AcceptToMemoryPool ( mempool , stateDummy , tx , false , NULL , true ) ) {
mempool . removeRecursive ( tx , removed ) ;
} else if ( mempool . exists ( tx . GetHash ( ) ) ) {
vHashUpdate . push_back ( tx . GetHash ( ) ) ;
}
}
// 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 ) ;
if ( ! fBare ) {
// Resurrect mempool transactions from the disconnected block.
std : : vector < uint256 > vHashUpdate ;
BOOST_FOREACH ( const CTransaction & tx , block . vtx ) {
// ignore validation errors in resurrected transactions
list < CTransaction > removed ;
CValidationState stateDummy ;
if ( tx . IsCoinBase ( ) | | ! AcceptToMemoryPool ( mempool , stateDummy , tx , false , NULL , true ) ) {
mempool . removeRecursive ( tx , removed ) ;
} else if ( mempool . exists ( tx . GetHash ( ) ) ) {
vHashUpdate . push_back ( tx . GetHash ( ) ) ;
}
}
// 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.
UpdateTip ( pindexDelete - > pprev , chainparams ) ;
// Let wallets know transactions went from 1-confirmed to
@ -3266,6 +3273,9 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
pindexNew - > nDataPos = pos . nPos ;
pindexNew - > nUndoPos = 0 ;
pindexNew - > nStatus | = BLOCK_HAVE_DATA ;
if ( IsWitnessEnabled ( pindexNew - > pprev , Params ( ) . GetConsensus ( ) ) ) {
pindexNew - > nStatus | = BLOCK_OPT_WITNESS ;
}
pindexNew - > RaiseValidity ( BLOCK_VALID_TRANSACTIONS ) ;
setDirtyBlockIndex . insert ( pindexNew ) ;
@ -4214,6 +4224,90 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
return true ;
}
bool RewindBlockIndex ( const CChainParams & params )
{
LOCK ( cs_main ) ;
int nHeight = 1 ;
while ( nHeight < = chainActive . Height ( ) ) {
if ( IsWitnessEnabled ( chainActive [ nHeight - 1 ] , params . GetConsensus ( ) ) & & ! ( chainActive [ nHeight ] - > nStatus & BLOCK_OPT_WITNESS ) ) {
break ;
}
nHeight + + ;
}
// nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
CValidationState state ;
CBlockIndex * pindex = chainActive . Tip ( ) ;
while ( chainActive . Height ( ) > = nHeight ) {
if ( fPruneMode & & ! ( chainActive . Tip ( ) - > nStatus & BLOCK_HAVE_DATA ) ) {
// If pruning, don't try rewinding past the HAVE_DATA point;
// since older blocks can't be served anyway, there's
// no need to walk further, and trying to DisconnectTip()
// will fail (and require a needless reindex/redownload
// of the blockchain).
break ;
}
if ( ! DisconnectTip ( state , params , true ) ) {
return error ( " RewindBlockIndex: unable to disconnect block at height %i " , pindex - > nHeight ) ;
}
// Occasionally flush state to disk.
if ( ! FlushStateToDisk ( state , FLUSH_STATE_PERIODIC ) )
return false ;
}
// Reduce validity flag and have-data flags.
// We do this after actual disconnecting, otherwise we'll end up writing the lack of data
// to disk before writing the chainstate, resulting in a failure to continue if interrupted.
for ( BlockMap : : iterator it = mapBlockIndex . begin ( ) ; it ! = mapBlockIndex . end ( ) ; it + + ) {
CBlockIndex * pindexIter = it - > second ;
// Note: If we encounter an insufficiently validated block that
// is on chainActive, it must be because we are a pruning node, and
// this block or some successor doesn't HAVE_DATA, so we were unable to
// rewind all the way. Blocks remaining on chainActive at this point
// must not have their validity reduced.
if ( IsWitnessEnabled ( pindexIter - > pprev , params . GetConsensus ( ) ) & & ! ( pindexIter - > nStatus & BLOCK_OPT_WITNESS ) & & ! chainActive . Contains ( pindexIter ) ) {
// Reduce validity
pindexIter - > nStatus = std : : min < unsigned int > ( pindexIter - > nStatus & BLOCK_VALID_MASK , BLOCK_VALID_TREE ) | ( pindexIter - > nStatus & ~ BLOCK_VALID_MASK ) ;
// Remove have-data flags.
pindexIter - > nStatus & = ~ ( BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO ) ;
// Remove storage location.
pindexIter - > nFile = 0 ;
pindexIter - > nDataPos = 0 ;
pindexIter - > nUndoPos = 0 ;
// Remove various other things
pindexIter - > nTx = 0 ;
pindexIter - > nChainTx = 0 ;
pindexIter - > nSequenceId = 0 ;
// Make sure it gets written.
setDirtyBlockIndex . insert ( pindexIter ) ;
// Update indexes
setBlockIndexCandidates . erase ( pindexIter ) ;
std : : pair < std : : multimap < CBlockIndex * , CBlockIndex * > : : iterator , std : : multimap < CBlockIndex * , CBlockIndex * > : : iterator > ret = mapBlocksUnlinked . equal_range ( pindexIter - > pprev ) ;
while ( ret . first ! = ret . second ) {
if ( ret . first - > second = = pindexIter ) {
mapBlocksUnlinked . erase ( ret . first + + ) ;
} else {
+ + ret . first ;
}
}
} else if ( pindexIter - > IsValid ( BLOCK_VALID_TRANSACTIONS ) & & pindexIter - > nChainTx ) {
setBlockIndexCandidates . insert ( pindexIter ) ;
}
}
PruneBlockIndexCandidates ( ) ;
CheckBlockIndex ( params . GetConsensus ( ) ) ;
if ( ! FlushStateToDisk ( state , FLUSH_STATE_ALWAYS ) ) {
return false ;
}
return true ;
}
void UnloadBlockIndex ( )
{
LOCK ( cs_main ) ;