aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2016-03-18 17:20:12 +0100
committerPieter Wuille <pieter.wuille@gmail.com>2016-06-22 15:43:00 +0200
commit6032f6930a56c107dad8f30c05fec4aab79c8c22 (patch)
tree7fd79172fe0bb65ef533bfb24b00f567d2706930
parentb7dbeb24ebff16198b2925d906c06771e167bd9e (diff)
Add rewind logic to deal with post-fork software updates
Includes logic for dealing with pruning by Suhas Daftuar.
-rw-r--r--src/chain.h2
-rw-r--r--src/init.cpp8
-rw-r--r--src/main.cpp132
-rw-r--r--src/main.h3
4 files changed, 126 insertions, 19 deletions
diff --git a/src/chain.h b/src/chain.h
index a13dae33d1..76a774c123 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -144,6 +144,8 @@ enum BlockStatus: uint32_t {
BLOCK_FAILED_VALID = 32, //! stage after last reached validness failed
BLOCK_FAILED_CHILD = 64, //! descends from failed block
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
+
+ BLOCK_OPT_WITNESS = 128, //! block data in blk*.data was received with a witness-enforcing client
};
/** The block chain is a tree shaped structure starting with the
diff --git a/src/init.cpp b/src/init.cpp
index e924d504bd..5d29f14eb8 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1284,6 +1284,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
break;
}
+ if (!fReindex) {
+ uiInterface.InitMessage(_("Rewinding blocks..."));
+ if (!RewindBlockIndex(chainparams)) {
+ strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
+ break;
+ }
+ }
+
uiInterface.InitMessage(_("Verifying blocks..."));
if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks",
diff --git a/src/main.cpp b/src/main.cpp
index df758bc41f..ec43a66701 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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);
diff --git a/src/main.h b/src/main.h
index 317470e3c8..84a6044bc9 100644
--- a/src/main.h
+++ b/src/main.h
@@ -459,6 +459,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
/** Check whether witness commitments are required for block. */
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
+/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
+bool RewindBlockIndex(const CChainParams& params);
+
/** Update uncommitted block structures (currently: only the witness nonce). This is safe for submitted blocks. */
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);