@ -1526,28 +1526,36 @@ bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint
return fClean ;
return fClean ;
}
}
enum DisconnectResult
{
DISCONNECT_OK , // All good.
DISCONNECT_UNCLEAN , // Rolled back, but UTXO set was inconsistent with block.
DISCONNECT_FAILED // Something else went wrong.
} ;
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
* In case pfClean is provided , operation will try to be tolerant about errors , and * pfClean
* When UNCLEAN or FAILED is returned , view is left in an indeterminate state . */
* will be true if no problems were found . Otherwise , the return value will be false in case
static DisconnectResult DisconnectBlock ( const CBlock & block , const CBlockIndex * pindex , CCoinsViewCache & view )
* of problems . Note that in any case , coins may be modified . */
static bool DisconnectBlock ( const CBlock & block , CValidationState & state , const CBlockIndex * pindex , CCoinsViewCache & view , bool * pfClean = NULL )
{
{
assert ( pindex - > GetBlockHash ( ) = = view . GetBestBlock ( ) ) ;
assert ( pindex - > GetBlockHash ( ) = = view . GetBestBlock ( ) ) ;
if ( pfClean )
* pfClean = false ;
bool fClean = true ;
bool fClean = true ;
CBlockUndo blockUndo ;
CBlockUndo blockUndo ;
CDiskBlockPos pos = pindex - > GetUndoPos ( ) ;
CDiskBlockPos pos = pindex - > GetUndoPos ( ) ;
if ( pos . IsNull ( ) )
if ( pos . IsNull ( ) ) {
return error ( " DisconnectBlock() : no undo data available " ) ;
error ( " DisconnectBlock(): no undo data available " ) ;
if ( ! UndoReadFromDisk ( blockUndo , pos , pindex - > pprev - > GetBlockHash ( ) ) )
return DISCONNECT_FAILED ;
return error ( " DisconnectBlock() : failure reading undo data " ) ;
}
if ( ! UndoReadFromDisk ( blockUndo , pos , pindex - > pprev - > GetBlockHash ( ) ) ) {
error ( " DisconnectBlock(): failure reading undo data " ) ;
return DISCONNECT_FAILED ;
}
if ( blockUndo . vtxundo . size ( ) + 1 ! = block . vtx . size ( ) )
if ( blockUndo . vtxundo . size ( ) + 1 ! = block . vtx . size ( ) ) {
return error ( " DisconnectBlock() : block and undo data inconsistent " ) ;
error ( " DisconnectBlock(): block and undo data inconsistent " ) ;
return DISCONNECT_FAILED ;
}
// undo transactions in reverse order
// undo transactions in reverse order
for ( int i = block . vtx . size ( ) - 1 ; i > = 0 ; i - - ) {
for ( int i = block . vtx . size ( ) - 1 ; i > = 0 ; i - - ) {
@ -1576,8 +1584,10 @@ static bool DisconnectBlock(const CBlock& block, CValidationState& state, const
// restore inputs
// restore inputs
if ( i > 0 ) { // not coinbases
if ( i > 0 ) { // not coinbases
const CTxUndo & txundo = blockUndo . vtxundo [ i - 1 ] ;
const CTxUndo & txundo = blockUndo . vtxundo [ i - 1 ] ;
if ( txundo . vprevout . size ( ) ! = tx . vin . size ( ) )
if ( txundo . vprevout . size ( ) ! = tx . vin . size ( ) ) {
return error ( " DisconnectBlock() : transaction and undo data inconsistent " ) ;
error ( " DisconnectBlock(): transaction and undo data inconsistent " ) ;
return DISCONNECT_FAILED ;
}
for ( unsigned int j = tx . vin . size ( ) ; j - - > 0 ; ) {
for ( unsigned int j = tx . vin . size ( ) ; j - - > 0 ; ) {
const COutPoint & out = tx . vin [ j ] . prevout ;
const COutPoint & out = tx . vin [ j ] . prevout ;
const CTxInUndo & undo = txundo . vprevout [ j ] ;
const CTxInUndo & undo = txundo . vprevout [ j ] ;
@ -1590,12 +1600,7 @@ static bool DisconnectBlock(const CBlock& block, CValidationState& state, const
// move best block pointer to prevout block
// move best block pointer to prevout block
view . SetBestBlock ( pindex - > pprev - > GetBlockHash ( ) ) ;
view . SetBestBlock ( pindex - > pprev - > GetBlockHash ( ) ) ;
if ( pfClean ) {
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN ;
* pfClean = fClean ;
return true ;
}
return fClean ;
}
}
void static FlushBlockFile ( bool fFinalize = false )
void static FlushBlockFile ( bool fFinalize = false )
@ -2131,7 +2136,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
int64_t nStart = GetTimeMicros ( ) ;
int64_t nStart = GetTimeMicros ( ) ;
{
{
CCoinsViewCache view ( pcoinsTip ) ;
CCoinsViewCache view ( pcoinsTip ) ;
if ( ! DisconnectBlock ( block , state , pindexDelete , view ) )
if ( DisconnectBlock ( block , pindexDelete , view ) ! = DISCONNECT_OK )
return error ( " DisconnectTip() : DisconnectBlock % s failed " , pindexDelete->GetBlockHash().ToString()) ;
return error ( " DisconnectTip() : DisconnectBlock % s failed " , pindexDelete->GetBlockHash().ToString()) ;
bool flushed = view . Flush ( ) ;
bool flushed = view . Flush ( ) ;
assert ( flushed ) ;
assert ( flushed ) ;
@ -3656,16 +3661,18 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if ( nCheckLevel > = 3 & & pindex = = pindexState & & ( coins . DynamicMemoryUsage ( ) + pcoinsTip - > DynamicMemoryUsage ( ) ) < = nCoinCacheUsage ) {
if ( nCheckLevel > = 3 & & pindex = = pindexState & & ( coins . DynamicMemoryUsage ( ) + pcoinsTip - > DynamicMemoryUsage ( ) ) < = nCoinCacheUsage ) {
bool fClean = true ;
DisconnectResult res = DisconnectBlock ( block , pindex , coins ) ;
if ( ! DisconnectBlock ( block , state , pindex , coins , & fClean ) )
if ( res = = DISCONNECT_FAILED ) {
return error ( " VerifyDB() : * * * irrecoverable inconsistency in block data at % d , hash = % s " , pindex->nHeight, pindex->GetBlockHash().ToString()) ;
return error ( " VerifyDB() : * * * irrecoverable inconsistency in block data at % d , hash = % s " , pindex->nHeight, pindex->GetBlockHash().ToString()) ;
}
pindexState = pindex - > pprev ;
pindexState = pindex - > pprev ;
if ( ! fClean ) {
if ( res = = DISCONNECT_UNCLEAN ) {
nGoodTransactions = 0 ;
nGoodTransactions = 0 ;
pindexFailure = pindex ;
pindexFailure = pindex ;
} else
} else {
nGoodTransactions + = block . vtx . size ( ) ;
nGoodTransactions + = block . vtx . size ( ) ;
}
}
}
if ( ShutdownRequested ( ) )
if ( ShutdownRequested ( ) )
return true ;
return true ;
}
}