Adjust rewind logic to use the network upgrade mechanism
This commit is contained in:
14
src/chain.h
14
src/chain.h
@@ -95,9 +95,13 @@ enum BlockStatus: uint32_t {
|
|||||||
BLOCK_FAILED_CHILD = 64, //! descends from failed block
|
BLOCK_FAILED_CHILD = 64, //! descends from failed block
|
||||||
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
|
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
|
BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Short-hand for the highest consensus validity we implement.
|
||||||
|
//! Blocks with this validity are assumed to satisfy all consensus rules.
|
||||||
|
static const BlockStatus BLOCK_VALID_CONSENSUS = BLOCK_VALID_SCRIPTS;
|
||||||
|
|
||||||
/** The block chain is a tree shaped structure starting with the
|
/** The block chain is a tree shaped structure starting with the
|
||||||
* genesis block at the root, with each block potentially having multiple
|
* genesis block at the root, with each block potentially having multiple
|
||||||
* candidates to be the next block. A blockindex may have multiple pprev pointing
|
* candidates to be the next block. A blockindex may have multiple pprev pointing
|
||||||
@@ -142,6 +146,11 @@ public:
|
|||||||
//! Verification status of this block. See enum BlockStatus
|
//! Verification status of this block. See enum BlockStatus
|
||||||
unsigned int nStatus;
|
unsigned int nStatus;
|
||||||
|
|
||||||
|
//! Branch ID corresponding to the consensus rules used to validate this block.
|
||||||
|
//! Only accurate if block validity is BLOCK_VALID_CONSENSUS.
|
||||||
|
//! Persisted at each activation height, memory-only for intervening blocks.
|
||||||
|
uint32_t nConsensusBranchId;
|
||||||
|
|
||||||
//! The anchor for the tree state up to the start of this block
|
//! The anchor for the tree state up to the start of this block
|
||||||
uint256 hashAnchor;
|
uint256 hashAnchor;
|
||||||
|
|
||||||
@@ -182,6 +191,7 @@ public:
|
|||||||
nTx = 0;
|
nTx = 0;
|
||||||
nChainTx = 0;
|
nChainTx = 0;
|
||||||
nStatus = 0;
|
nStatus = 0;
|
||||||
|
nConsensusBranchId = 0;
|
||||||
hashAnchor = uint256();
|
hashAnchor = uint256();
|
||||||
hashAnchorEnd = uint256();
|
hashAnchorEnd = uint256();
|
||||||
nSequenceId = 0;
|
nSequenceId = 0;
|
||||||
@@ -343,6 +353,8 @@ public:
|
|||||||
READWRITE(VARINT(nDataPos));
|
READWRITE(VARINT(nDataPos));
|
||||||
if (nStatus & BLOCK_HAVE_UNDO)
|
if (nStatus & BLOCK_HAVE_UNDO)
|
||||||
READWRITE(VARINT(nUndoPos));
|
READWRITE(VARINT(nUndoPos));
|
||||||
|
if (nStatus & BLOCK_ACTIVATES_UPGRADE)
|
||||||
|
READWRITE(nConsensusBranchId);
|
||||||
READWRITE(hashAnchor);
|
READWRITE(hashAnchor);
|
||||||
|
|
||||||
// block header
|
// block header
|
||||||
|
|||||||
@@ -1425,7 +1425,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||||||
if (!fReindex) {
|
if (!fReindex) {
|
||||||
uiInterface.InitMessage(_("Rewinding blocks..."));
|
uiInterface.InitMessage(_("Rewinding blocks..."));
|
||||||
if (!RewindBlockIndex(chainparams)) {
|
if (!RewindBlockIndex(chainparams)) {
|
||||||
strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain");
|
strLoadError = _("Unable to rewind the database to a pre-upgrade state. You will need to redownload the blockchain");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
61
src/main.cpp
61
src/main.cpp
@@ -13,6 +13,7 @@
|
|||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "checkqueue.h"
|
#include "checkqueue.h"
|
||||||
|
#include "consensus/upgrades.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
#include "deprecation.h"
|
#include "deprecation.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
@@ -2222,6 +2223,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
pindex->nStatus |= BLOCK_HAVE_UNDO;
|
pindex->nStatus |= BLOCK_HAVE_UNDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that all consensus rules have been validated, set nConsensusBranchId.
|
||||||
|
// Move this if BLOCK_VALID_CONSENSUS is ever altered.
|
||||||
|
static_assert(BLOCK_VALID_CONSENSUS == BLOCK_VALID_SCRIPTS,
|
||||||
|
"nConsensusBranchId must be set after all consensus rules have been validated.");
|
||||||
|
if (IsActivationHeightForAnyUpgrade(pindex->nHeight, Params().GetConsensus())) {
|
||||||
|
pindex->nStatus |= BLOCK_ACTIVATES_UPGRADE;
|
||||||
|
pindex->nConsensusBranchId = CurrentEpochBranchId(pindex->nHeight, chainparams.GetConsensus());
|
||||||
|
} else if (pindex->pprev) {
|
||||||
|
pindex->nConsensusBranchId = pindex->pprev->nConsensusBranchId;
|
||||||
|
}
|
||||||
|
|
||||||
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
|
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
|
||||||
setDirtyBlockIndex.insert(pindex);
|
setDirtyBlockIndex.insert(pindex);
|
||||||
}
|
}
|
||||||
@@ -2855,9 +2867,6 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
|
|||||||
pindexNew->nDataPos = pos.nPos;
|
pindexNew->nDataPos = pos.nPos;
|
||||||
pindexNew->nUndoPos = 0;
|
pindexNew->nUndoPos = 0;
|
||||||
pindexNew->nStatus |= BLOCK_HAVE_DATA;
|
pindexNew->nStatus |= BLOCK_HAVE_DATA;
|
||||||
if (IsWitnessEnabled(pindexNew->pprev, Params().GetConsensus())) {
|
|
||||||
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
|
|
||||||
}
|
|
||||||
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
|
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
|
||||||
setDirtyBlockIndex.insert(pindexNew);
|
setDirtyBlockIndex.insert(pindexNew);
|
||||||
|
|
||||||
@@ -3565,6 +3574,13 @@ bool static LoadBlockIndexDB()
|
|||||||
pindex->nChainSproutValue = pindex->nSproutValue;
|
pindex->nChainSproutValue = pindex->nSproutValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Construct in-memory chain of branch IDs.
|
||||||
|
// Relies on invariant: a block that does not activate a network upgrade
|
||||||
|
// will always be valid under the same consensus rules as its parent.
|
||||||
|
// Activation blocks will have non-zero branch IDs (read from disk).
|
||||||
|
if (pindex->IsValid(BLOCK_VALID_CONSENSUS) && pindex->nConsensusBranchId == 0 && pindex->pprev) {
|
||||||
|
pindex->nConsensusBranchId = pindex->pprev->nConsensusBranchId;
|
||||||
|
}
|
||||||
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL))
|
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL))
|
||||||
setBlockIndexCandidates.insert(pindex);
|
setBlockIndexCandidates.insert(pindex);
|
||||||
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
|
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
|
||||||
@@ -3752,9 +3768,25 @@ bool RewindBlockIndex(const CChainParams& params)
|
|||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
|
// RewindBlockIndex is called after LoadBlockIndex, so at this point every block
|
||||||
|
// index will have nConsensusBranchId set based on the values previously persisted
|
||||||
|
// to disk. By definition, a non-zero nConsensusBranchId means that the block was
|
||||||
|
// fully-validated under the corresponding consensus rules. Thus we can quickly
|
||||||
|
// identify whether the current active chain matches our expected sequence of
|
||||||
|
// consensus rule changes, with two checks:
|
||||||
|
//
|
||||||
|
// - BLOCK_ACTIVATES_UPGRADE is set only on blocks that activate upgrades.
|
||||||
|
// - nConsensusBranchId for each block matches what we expect.
|
||||||
|
auto sufficientlyValidated = [¶ms](const CBlockIndex* pindex) {
|
||||||
|
auto consensus = params.GetConsensus();
|
||||||
|
bool fFlagSet = pindex->nStatus & BLOCK_ACTIVATES_UPGRADE;
|
||||||
|
bool fFlagExpected = IsActivationHeightForAnyUpgrade(pindex->nHeight, consensus);
|
||||||
|
return fFlagSet == fFlagExpected && pindex->nConsensusBranchId == CurrentEpochBranchId(pindex->nHeight, consensus);
|
||||||
|
};
|
||||||
|
|
||||||
int nHeight = 1;
|
int nHeight = 1;
|
||||||
while (nHeight <= chainActive.Height()) {
|
while (nHeight <= chainActive.Height()) {
|
||||||
if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
|
if (!sufficientlyValidated(chainActive[nHeight])) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
nHeight++;
|
nHeight++;
|
||||||
@@ -3791,24 +3823,31 @@ bool RewindBlockIndex(const CChainParams& params)
|
|||||||
// this block or some successor doesn't HAVE_DATA, so we were unable to
|
// 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
|
// rewind all the way. Blocks remaining on chainActive at this point
|
||||||
// must not have their validity reduced.
|
// must not have their validity reduced.
|
||||||
if (IsWitnessEnabled(pindexIter->pprev, params.GetConsensus()) && !(pindexIter->nStatus & BLOCK_OPT_WITNESS) && !chainActive.Contains(pindexIter)) {
|
if (!sufficientlyValidated(pindexIter) && !chainActive.Contains(pindexIter)) {
|
||||||
// Reduce validity
|
// Reduce validity
|
||||||
pindexIter->nStatus = std::min<unsigned int>(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK);
|
pindexIter->nStatus =
|
||||||
// Remove have-data flags.
|
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);
|
pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
|
||||||
// Remove storage location.
|
// Remove branch ID
|
||||||
|
pindexIter->nStatus &= ~BLOCK_ACTIVATES_UPGRADE;
|
||||||
|
pindexIter->nConsensusBranchId = 0;
|
||||||
|
// Remove storage location
|
||||||
pindexIter->nFile = 0;
|
pindexIter->nFile = 0;
|
||||||
pindexIter->nDataPos = 0;
|
pindexIter->nDataPos = 0;
|
||||||
pindexIter->nUndoPos = 0;
|
pindexIter->nUndoPos = 0;
|
||||||
// Remove various other things
|
// Remove various other things
|
||||||
pindexIter->nTx = 0;
|
pindexIter->nTx = 0;
|
||||||
pindexIter->nChainTx = 0;
|
pindexIter->nChainTx = 0;
|
||||||
|
pindexIter->nSproutValue = boost::none;
|
||||||
|
pindexIter->nChainSproutValue = boost::none;
|
||||||
pindexIter->nSequenceId = 0;
|
pindexIter->nSequenceId = 0;
|
||||||
// Make sure it gets written.
|
// Make sure it gets written
|
||||||
setDirtyBlockIndex.insert(pindexIter);
|
setDirtyBlockIndex.insert(pindexIter);
|
||||||
// Update indexes
|
// Update indices
|
||||||
setBlockIndexCandidates.erase(pindexIter);
|
setBlockIndexCandidates.erase(pindexIter);
|
||||||
std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> ret = mapBlocksUnlinked.equal_range(pindexIter->pprev);
|
auto ret = mapBlocksUnlinked.equal_range(pindexIter->pprev);
|
||||||
while (ret.first != ret.second) {
|
while (ret.first != ret.second) {
|
||||||
if (ret.first->second == pindexIter) {
|
if (ret.first->second == pindexIter) {
|
||||||
mapBlocksUnlinked.erase(ret.first++);
|
mapBlocksUnlinked.erase(ret.first++);
|
||||||
|
|||||||
@@ -436,7 +436,11 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */
|
/**
|
||||||
|
* When there are blocks in the active chain with missing data (e.g. if the
|
||||||
|
* activation height and branch ID of a particular upgrade have been altered),
|
||||||
|
* rewind the chainstate and remove them from the block index.
|
||||||
|
*/
|
||||||
bool RewindBlockIndex(const CChainParams& params);
|
bool RewindBlockIndex(const CChainParams& params);
|
||||||
|
|
||||||
class CBlockFileInfo
|
class CBlockFileInfo
|
||||||
|
|||||||
@@ -309,6 +309,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
|
|||||||
pindexNew->nNonce = diskindex.nNonce;
|
pindexNew->nNonce = diskindex.nNonce;
|
||||||
pindexNew->nSolution = diskindex.nSolution;
|
pindexNew->nSolution = diskindex.nSolution;
|
||||||
pindexNew->nStatus = diskindex.nStatus;
|
pindexNew->nStatus = diskindex.nStatus;
|
||||||
|
pindexNew->nConsensusBranchId = diskindex.nConsensusBranchId;
|
||||||
pindexNew->nTx = diskindex.nTx;
|
pindexNew->nTx = diskindex.nTx;
|
||||||
pindexNew->nSproutValue = diskindex.nSproutValue;
|
pindexNew->nSproutValue = diskindex.nSproutValue;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user