Skip to content

Commit 6032f69

Browse files
committed
Add rewind logic to deal with post-fork software updates
Includes logic for dealing with pruning by Suhas Daftuar.
1 parent b7dbeb2 commit 6032f69

File tree

4 files changed

+126
-19
lines changed

4 files changed

+126
-19
lines changed

src/chain.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ enum BlockStatus: uint32_t {
144144
BLOCK_FAILED_VALID = 32, //! stage after last reached validness failed
145145
BLOCK_FAILED_CHILD = 64, //! descends from failed block
146146
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
147+
148+
BLOCK_OPT_WITNESS = 128, //! block data in blk*.data was received with a witness-enforcing client
147149
};
148150

149151
/** The block chain is a tree shaped structure starting with the

src/init.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
12841284
break;
12851285
}
12861286

1287+
if (!fReindex) {
1288+
uiInterface.InitMessage(_("Rewinding blocks..."));
1289+
if (!RewindBlockIndex(chainparams)) {
1290+
strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
1291+
break;
1292+
}
1293+
}
1294+
12871295
uiInterface.InitMessage(_("Verifying blocks..."));
12881296
if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
12891297
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks",

src/main.cpp

Lines changed: 113 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,9 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
658658
CBlockIndex* pindex = (*mi).second;
659659
if (chain.Contains(pindex))
660660
return pindex;
661+
if (pindex->GetAncestor(chain.Height()) == chain.Tip()) {
662+
return chain.Tip();
663+
}
661664
}
662665
}
663666
return chain.Genesis();
@@ -2777,7 +2780,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
27772780
}
27782781

27792782
/** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */
2780-
bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams)
2783+
bool static DisconnectTip(CValidationState& state, const CChainParams& chainparams, bool fBare = false)
27812784
{
27822785
CBlockIndex *pindexDelete = chainActive.Tip();
27832786
assert(pindexDelete);
@@ -2797,24 +2800,28 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
27972800
// Write the chain state to disk, if necessary.
27982801
if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
27992802
return false;
2800-
// Resurrect mempool transactions from the disconnected block.
2801-
std::vector<uint256> vHashUpdate;
2802-
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
2803-
// ignore validation errors in resurrected transactions
2804-
list<CTransaction> removed;
2805-
CValidationState stateDummy;
2806-
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
2807-
mempool.removeRecursive(tx, removed);
2808-
} else if (mempool.exists(tx.GetHash())) {
2809-
vHashUpdate.push_back(tx.GetHash());
2810-
}
2811-
}
2812-
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
2813-
// no in-mempool children, which is generally not true when adding
2814-
// previously-confirmed transactions back to the mempool.
2815-
// UpdateTransactionsFromBlock finds descendants of any transactions in this
2816-
// block that were added back and cleans up the mempool state.
2817-
mempool.UpdateTransactionsFromBlock(vHashUpdate);
2803+
2804+
if (!fBare) {
2805+
// Resurrect mempool transactions from the disconnected block.
2806+
std::vector<uint256> vHashUpdate;
2807+
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
2808+
// ignore validation errors in resurrected transactions
2809+
list<CTransaction> removed;
2810+
CValidationState stateDummy;
2811+
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
2812+
mempool.removeRecursive(tx, removed);
2813+
} else if (mempool.exists(tx.GetHash())) {
2814+
vHashUpdate.push_back(tx.GetHash());
2815+
}
2816+
}
2817+
// AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
2818+
// no in-mempool children, which is generally not true when adding
2819+
// previously-confirmed transactions back to the mempool.
2820+
// UpdateTransactionsFromBlock finds descendants of any transactions in this
2821+
// block that were added back and cleans up the mempool state.
2822+
mempool.UpdateTransactionsFromBlock(vHashUpdate);
2823+
}
2824+
28182825
// Update chainActive and related variables.
28192826
UpdateTip(pindexDelete->pprev, chainparams);
28202827
// Let wallets know transactions went from 1-confirmed to
@@ -3266,6 +3273,9 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
32663273
pindexNew->nDataPos = pos.nPos;
32673274
pindexNew->nUndoPos = 0;
32683275
pindexNew->nStatus |= BLOCK_HAVE_DATA;
3276+
if (IsWitnessEnabled(pindexNew->pprev, Params().GetConsensus())) {
3277+
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
3278+
}
32693279
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
32703280
setDirtyBlockIndex.insert(pindexNew);
32713281

@@ -4214,6 +4224,90 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
42144224
return true;
42154225
}
42164226

4227+
bool RewindBlockIndex(const CChainParams& params)
4228+
{
4229+
LOCK(cs_main);
4230+
4231+
int nHeight = 1;
4232+
while (nHeight <= chainActive.Height()) {
4233+
if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
4234+
break;
4235+
}
4236+
nHeight++;
4237+
}
4238+
4239+
// nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
4240+
CValidationState state;
4241+
CBlockIndex* pindex = chainActive.Tip();
4242+
while (chainActive.Height() >= nHeight) {
4243+
if (fPruneMode && !(chainActive.Tip()->nStatus & BLOCK_HAVE_DATA)) {
4244+
// If pruning, don't try rewinding past the HAVE_DATA point;
4245+
// since older blocks can't be served anyway, there's
4246+
// no need to walk further, and trying to DisconnectTip()
4247+
// will fail (and require a needless reindex/redownload
4248+
// of the blockchain).
4249+
break;
4250+
}
4251+
if (!DisconnectTip(state, params, true)) {
4252+
return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight);
4253+
}
4254+
// Occasionally flush state to disk.
4255+
if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC))
4256+
return false;
4257+
}
4258+
4259+
// Reduce validity flag and have-data flags.
4260+
// We do this after actual disconnecting, otherwise we'll end up writing the lack of data
4261+
// to disk before writing the chainstate, resulting in a failure to continue if interrupted.
4262+
for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) {
4263+
CBlockIndex* pindexIter = it->second;
4264+
4265+
// Note: If we encounter an insufficiently validated block that
4266+
// is on chainActive, it must be because we are a pruning node, and
4267+
// this block or some successor doesn't HAVE_DATA, so we were unable to
4268+
// rewind all the way. Blocks remaining on chainActive at this point
4269+
// must not have their validity reduced.
4270+
if (IsWitnessEnabled(pindexIter->pprev, params.GetConsensus()) && !(pindexIter->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(pindexIter)) {
4271+
// Reduce validity
4272+
pindexIter->nStatus = std::min<unsigned int>(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK);
4273+
// Remove have-data flags.
4274+
pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
4275+
// Remove storage location.
4276+
pindexIter->nFile = 0;
4277+
pindexIter->nDataPos = 0;
4278+
pindexIter->nUndoPos = 0;
4279+
// Remove various other things
4280+
pindexIter->nTx = 0;
4281+
pindexIter->nChainTx = 0;
4282+
pindexIter->nSequenceId = 0;
4283+
// Make sure it gets written.
4284+
setDirtyBlockIndex.insert(pindexIter);
4285+
// Update indexes
4286+
setBlockIndexCandidates.erase(pindexIter);
4287+
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(pindexIter->pprev);
4288+
while (ret.first != ret.second) {
4289+
if (ret.first->second == pindexIter) {
4290+
mapBlocksUnlinked.erase(ret.first++);
4291+
} else {
4292+
++ret.first;
4293+
}
4294+
}
4295+
} else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) {
4296+
setBlockIndexCandidates.insert(pindexIter);
4297+
}
4298+
}
4299+
4300+
PruneBlockIndexCandidates();
4301+
4302+
CheckBlockIndex(params.GetConsensus());
4303+
4304+
if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) {
4305+
return false;
4306+
}
4307+
4308+
return true;
4309+
}
4310+
42174311
void UnloadBlockIndex()
42184312
{
42194313
LOCK(cs_main);

src/main.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
459459
/** Check whether witness commitments are required for block. */
460460
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
461461

462+
/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
463+
bool RewindBlockIndex(const CChainParams& params);
464+
462465
/** Update uncommitted block structures (currently: only the witness nonce). This is safe for submitted blocks. */
463466
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);
464467

0 commit comments

Comments
 (0)