Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
#include <boost/algorithm/string/replace.hpp> | #include <boost/algorithm/string/replace.hpp> | ||||
#include <boost/range/adaptor/reversed.hpp> | #include <boost/range/adaptor/reversed.hpp> | ||||
#include <boost/thread.hpp> | #include <boost/thread.hpp> | ||||
#if defined(NDEBUG) | #if defined(NDEBUG) | ||||
#error "Bitcoin cannot be compiled without assertions." | #error "Bitcoin cannot be compiled without assertions." | ||||
#endif | #endif | ||||
class ConnectTrace; | |||||
/** | /** | ||||
* Global state | * CChainState stores and provides an API to update our local knowledge of the | ||||
* current best chain and header tree. | |||||
* | |||||
* It generally provides access to the current block tree, as well as functions | |||||
* to provide new data, which it will appropriately validate and incorporate in | |||||
* its state as necessary. | |||||
* | |||||
* Eventually, the API here is targeted at being exposed externally as a | |||||
* consumable libconsensus library, so any functions added must only call | |||||
* other class member functions, pure functions in other parts of the consensus | |||||
* library, callbacks via the validation interface, or read/write-to-disk | |||||
* functions (eventually this will also be via callbacks). | |||||
*/ | */ | ||||
CCriticalSection cs_main; | class CChainState { | ||||
private: | |||||
/** | |||||
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for | |||||
* itself and all ancestors) and | |||||
* as good as our current tip or better. Entries may be failed, though, and | |||||
* pruning nodes may be | |||||
* missing the data for the block. | |||||
*/ | |||||
std::set<CBlockIndex *, CBlockIndexWorkComparator> setBlockIndexCandidates; | |||||
BlockMap mapBlockIndex; | public: | ||||
CChain chainActive; | CChain chainActive; | ||||
BlockMap mapBlockIndex; | |||||
std::multimap<CBlockIndex *, CBlockIndex *> mapBlocksUnlinked; | |||||
CBlockIndex *pindexBestInvalid = nullptr; | |||||
CBlockIndex *pindexBestParked = nullptr; | |||||
bool LoadBlockIndex(const Config &config, CBlockTreeDB &blocktree); | |||||
bool ActivateBestChain( | |||||
const Config &config, CValidationState &state, | |||||
std::shared_ptr<const CBlock> pblock = std::shared_ptr<const CBlock>()); | |||||
bool AcceptBlockHeader(const Config &config, const CBlockHeader &block, | |||||
CValidationState &state, CBlockIndex **ppindex); | |||||
bool AcceptBlock(const Config &config, | |||||
const std::shared_ptr<const CBlock> &pblock, | |||||
CValidationState &state, CBlockIndex **ppindex, | |||||
bool fRequested, const CDiskBlockPos *dbp, | |||||
bool *fNewBlock); | |||||
// Block (dis)connection on a given view: | |||||
DisconnectResult DisconnectBlock(const CBlock &block, | |||||
const CBlockIndex *pindex, | |||||
CCoinsViewCache &view); | |||||
bool ConnectBlock(const Config &config, const CBlock &block, | |||||
CValidationState &state, CBlockIndex *pindex, | |||||
CCoinsViewCache &view, bool fJustCheck = false); | |||||
// Block disconnection on our pcoinsTip: | |||||
bool DisconnectTip(const Config &config, CValidationState &state, | |||||
DisconnectedBlockTransactions *disconnectpool); | |||||
// Manual block validity manipulation: | |||||
bool PreciousBlock(const Config &config, CValidationState &state, | |||||
CBlockIndex *pindex); | |||||
bool UnwindBlock(const Config &config, CValidationState &state, | |||||
CBlockIndex *pindex, bool invalidate); | |||||
bool ResetBlockFailureFlags(CBlockIndex *pindex); | |||||
template <typename F> bool UpdateFlags(CBlockIndex *pindex, F f); | |||||
/** Park a block. */ | |||||
bool ParkBlock(const Config &config, CValidationState &state, | |||||
CBlockIndex *pindex); | |||||
/** Remove parked status from a block and its descendants. */ | |||||
bool UnparkBlock(CBlockIndex *pindex); | |||||
bool ReplayBlocks(const Config &config, CCoinsView *view); | |||||
bool RewindBlockIndex(const Config &config); | |||||
bool LoadGenesisBlock(const CChainParams &chainparams); | |||||
void PruneBlockIndexCandidates(); | |||||
void UnloadBlockIndex(); | |||||
private: | |||||
bool ActivateBestChainStep(const Config &config, CValidationState &state, | |||||
CBlockIndex *pindexMostWork, | |||||
const std::shared_ptr<const CBlock> &pblock, | |||||
bool &fInvalidFound, ConnectTrace &connectTrace); | |||||
bool ConnectTip(const Config &config, CValidationState &state, | |||||
CBlockIndex *pindexNew, | |||||
const std::shared_ptr<const CBlock> &pblock, | |||||
ConnectTrace &connectTrace, | |||||
DisconnectedBlockTransactions &disconnectpool); | |||||
CBlockIndex *AddToBlockIndex(const CBlockHeader &block); | |||||
/** Create a new block index entry for a given block hash */ | |||||
CBlockIndex *InsertBlockIndex(const uint256 &hash); | |||||
void CheckBlockIndex(const Consensus::Params &consensusParams); | |||||
void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state); | |||||
CBlockIndex *FindMostWorkChain(); | |||||
bool ReceivedBlockTransactions(const CBlock &block, CValidationState &state, | |||||
CBlockIndex *pindexNew, | |||||
const CDiskBlockPos &pos); | |||||
bool RollforwardBlock(const CBlockIndex *pindex, CCoinsViewCache &inputs, | |||||
const Config &config); | |||||
} g_chainstate; | |||||
CCriticalSection cs_main; | |||||
BlockMap &mapBlockIndex = g_chainstate.mapBlockIndex; | |||||
CChain &chainActive = g_chainstate.chainActive; | |||||
CBlockIndex *pindexBestHeader = nullptr; | CBlockIndex *pindexBestHeader = nullptr; | ||||
CWaitableCriticalSection g_best_block_mutex; | CWaitableCriticalSection g_best_block_mutex; | ||||
CConditionVariable g_best_block_cv; | CConditionVariable g_best_block_cv; | ||||
uint256 g_best_block; | uint256 g_best_block; | ||||
int nScriptCheckThreads = 0; | int nScriptCheckThreads = 0; | ||||
std::atomic_bool fImporting(false); | std::atomic_bool fImporting(false); | ||||
bool fReindex = false; | std::atomic_bool fReindex(false); | ||||
bool fTxIndex = false; | bool fTxIndex = false; | ||||
bool fHavePruned = false; | bool fHavePruned = false; | ||||
bool fPruneMode = false; | bool fPruneMode = false; | ||||
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; | bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; | ||||
bool fRequireStandard = true; | bool fRequireStandard = true; | ||||
bool fCheckBlockIndex = false; | bool fCheckBlockIndex = false; | ||||
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; | bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; | ||||
size_t nCoinCacheUsage = 5000 * 300; | size_t nCoinCacheUsage = 5000 * 300; | ||||
uint64_t nPruneTarget = 0; | uint64_t nPruneTarget = 0; | ||||
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; | int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; | ||||
uint256 hashAssumeValid; | uint256 hashAssumeValid; | ||||
arith_uint256 nMinimumChainWork; | arith_uint256 nMinimumChainWork; | ||||
Amount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; | Amount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; | ||||
CTxMemPool mempool; | CTxMemPool mempool; | ||||
static void CheckBlockIndex(const Consensus::Params &consensusParams); | |||||
/** Constant stuff for coinbase transactions we create: */ | /** Constant stuff for coinbase transactions we create: */ | ||||
CScript COINBASE_FLAGS; | CScript COINBASE_FLAGS; | ||||
const std::string strMessageMagic = "Bitcoin Signed Message:\n"; | const std::string strMessageMagic = "Bitcoin Signed Message:\n"; | ||||
// Internal stuff | // Internal stuff | ||||
namespace { | namespace { | ||||
CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid; | |||||
CBlockIndex *&pindexBestParked = g_chainstate.pindexBestParked; | |||||
CBlockIndex *pindexBestInvalid; | /** All pairs A->B, where A (or one of its ancestors) misses transactions, but B | ||||
CBlockIndex *pindexBestParked; | * has transactions. | ||||
* Pruned nodes may have entries where B is missing data. | |||||
/** | |||||
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself | |||||
* and all ancestors) and as good as our current tip or better. Entries may be | |||||
* failed, though, and pruning nodes may be missing the data for the block. | |||||
*/ | |||||
std::set<CBlockIndex *, CBlockIndexWorkComparator> setBlockIndexCandidates; | |||||
/** | |||||
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B | |||||
* has transactions. Pruned nodes may have entries where B is missing data. | |||||
*/ | */ | ||||
std::multimap<CBlockIndex *, CBlockIndex *> mapBlocksUnlinked; | std::multimap<CBlockIndex *, CBlockIndex *> &mapBlocksUnlinked = | ||||
g_chainstate.mapBlocksUnlinked; | |||||
CCriticalSection cs_LastBlockFile; | CCriticalSection cs_LastBlockFile; | ||||
std::vector<CBlockFileInfo> vinfoBlockFile; | std::vector<CBlockFileInfo> vinfoBlockFile; | ||||
int nLastBlockFile = 0; | int nLastBlockFile = 0; | ||||
/** | /** Global flag to indicate we should check to see if there are | ||||
* Global flag to indicate we should check to see if there are block/undo files | * block/undo files that should be deleted. Set on startup | ||||
* that should be deleted. Set on startup or if we allocate more file space when | * or if we allocate more file space when we're in prune mode | ||||
* we're in prune mode. | |||||
*/ | */ | ||||
bool fCheckForPruning = false; | bool fCheckForPruning = false; | ||||
/** | /** | ||||
* Every received block is assigned a unique and increasing identifier, so we | * Every received block is assigned a unique and increasing identifier, so we | ||||
* know which one to give priority in case of a fork. | * know which one to give priority in case of a fork. | ||||
* Blocks loaded from disk are assigned id 0, so start the counter at 1. | |||||
*/ | */ | ||||
std::atomic<int32_t> nBlockSequenceId{1}; | CCriticalSection cs_nBlockSequenceId; | ||||
/** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ | |||||
int32_t nBlockSequenceId = 1; | |||||
/** Decreasing counter (used by subsequent preciousblock calls). */ | /** Decreasing counter (used by subsequent preciousblock calls). */ | ||||
int32_t nBlockReverseSequenceId = -1; | int32_t nBlockReverseSequenceId = -1; | ||||
/** chainwork for the last block that preciousblock has been applied to. */ | /** chainwork for the last block that preciousblock has been applied to. */ | ||||
arith_uint256 nLastPreciousChainwork = 0; | arith_uint256 nLastPreciousChainwork = 0; | ||||
/** In order to efficiently track invalidity of headers, we keep the set of | |||||
* blocks which we tried to connect and found to be invalid here (ie which | |||||
* were set to BLOCK_FAILED_VALID since the last restart). We can then | |||||
* walk this set and check if a new header is a descendant of something in | |||||
* this set, preventing us from having to walk mapBlockIndex when we try | |||||
* to connect a bad block and fail. | |||||
* | |||||
* While this is more complicated than marking everything which descends | |||||
* from an invalid block as invalid at the time we discover it to be | |||||
* invalid, doing so would require walking all of mapBlockIndex to find all | |||||
* descendants. Since this case should be very rare, keeping track of all | |||||
* BLOCK_FAILED_VALID blocks in a set should be just fine and work just as | |||||
* well. | |||||
* | |||||
* Because we already walk mapBlockIndex in height-order at startup, we go | |||||
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, | |||||
* instead of putting things in this set. | |||||
*/ | |||||
std::set<CBlockIndex *> g_failed_blocks; | |||||
/** Dirty block index entries. */ | /** Dirty block index entries. */ | ||||
std::set<const CBlockIndex *> setDirtyBlockIndex; | std::set<CBlockIndex *> setDirtyBlockIndex; | ||||
/** Dirty block file entries. */ | /** Dirty block file entries. */ | ||||
std::set<int> setDirtyFileInfo; | std::set<int> setDirtyFileInfo; | ||||
} // namespace | } // anon namespace | ||||
CBlockIndex *FindForkInGlobalIndex(const CChain &chain, | CBlockIndex *FindForkInGlobalIndex(const CChain &chain, | ||||
const CBlockLocator &locator) { | const CBlockLocator &locator) { | ||||
// Find the first block the caller has in the main chain | // Find the first block the caller has in the main chain | ||||
for (const uint256 &hash : locator.vHave) { | for (const uint256 &hash : locator.vHave) { | ||||
BlockMap::iterator mi = mapBlockIndex.find(hash); | BlockMap::iterator mi = mapBlockIndex.find(hash); | ||||
if (mi != mapBlockIndex.end()) { | if (mi != mapBlockIndex.end()) { | ||||
CBlockIndex *pindex = (*mi).second; | CBlockIndex *pindex = (*mi).second; | ||||
▲ Show 20 Lines • Show All 898 Lines • ▼ Show 20 Lines | static void InvalidChainFound(CBlockIndex *pindexNew) { | ||||
CBlockIndex *tip = chainActive.Tip(); | CBlockIndex *tip = chainActive.Tip(); | ||||
assert(tip); | assert(tip); | ||||
LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", | LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", | ||||
__func__, tip->GetBlockHash().ToString(), chainActive.Height(), | __func__, tip->GetBlockHash().ToString(), chainActive.Height(), | ||||
log(tip->nChainWork.getdouble()) / log(2.0), | log(tip->nChainWork.getdouble()) / log(2.0), | ||||
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); | DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime())); | ||||
} | } | ||||
static void InvalidBlockFound(CBlockIndex *pindex, | void CChainState::InvalidBlockFound(CBlockIndex *pindex, | ||||
const CValidationState &state) { | const CValidationState &state) { | ||||
if (!state.CorruptionPossible()) { | if (!state.CorruptionPossible()) { | ||||
pindex->nStatus = pindex->nStatus.withFailed(); | pindex->nStatus = pindex->nStatus.withFailed(); | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
InvalidChainFound(pindex); | InvalidChainFound(pindex); | ||||
} | } | ||||
} | } | ||||
void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, | void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, | ||||
▲ Show 20 Lines • Show All 229 Lines • ▼ Show 20 Lines | |||||
bool AbortNode(CValidationState &state, const std::string &strMessage, | bool AbortNode(CValidationState &state, const std::string &strMessage, | ||||
const std::string &userMessage = "") { | const std::string &userMessage = "") { | ||||
AbortNode(strMessage, userMessage); | AbortNode(strMessage, userMessage); | ||||
return state.Error(strMessage); | return state.Error(strMessage); | ||||
} | } | ||||
} // namespace | } // namespace | ||||
/** Restore the UTXO in a Coin at a given COutPoint. */ | /** | ||||
DisconnectResult UndoCoinSpend(const Coin &undo, CCoinsViewCache &view, | * Restore the UTXO in a Coin at a given COutPoint | ||||
const COutPoint &out) { | * @param undo The Coin to be restored. | ||||
* @param view The coins view to which to apply the changes. | |||||
* @param out The out point that corresponds to the tx input. | |||||
* @return A DisconnectResult as an int | |||||
*/ DisconnectResult | |||||
UndoCoinSpend(const Coin &undo, CCoinsViewCache &view, const COutPoint &out) { | |||||
bool fClean = true; | bool fClean = true; | ||||
if (view.HaveCoin(out)) { | if (view.HaveCoin(out)) { | ||||
// Overwriting transaction output. | // Overwriting transaction output. | ||||
fClean = false; | fClean = false; | ||||
} | } | ||||
if (undo.GetHeight() == 0) { | if (undo.GetHeight() == 0) { | ||||
Show All 22 Lines | UndoCoinSpend(const Coin &undo, CCoinsViewCache &view, const COutPoint &out) { | ||||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; | return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; | ||||
} | } | ||||
/** | /** | ||||
* Undo the effects of this block (with given index) on the UTXO set represented | * Undo the effects of this block (with given index) on the UTXO set represented | ||||
* by coins. When FAILED is returned, view is left in an indeterminate state. | * by coins. When FAILED is returned, view is left in an indeterminate state. | ||||
*/ | */ | ||||
static DisconnectResult DisconnectBlock(const CBlock &block, | DisconnectResult CChainState::DisconnectBlock(const CBlock &block, | ||||
const CBlockIndex *pindex, | const CBlockIndex *pindex, | ||||
CCoinsViewCache &view) { | CCoinsViewCache &view) { | ||||
CBlockUndo blockUndo; | CBlockUndo blockUndo; | ||||
if (!UndoReadFromDisk(blockUndo, pindex)) { | if (!UndoReadFromDisk(blockUndo, pindex)) { | ||||
error("DisconnectBlock(): failure reading undo data"); | error("DisconnectBlock(): failure reading undo data"); | ||||
return DISCONNECT_FAILED; | return DISCONNECT_FAILED; | ||||
} | } | ||||
return ApplyBlockUndo(blockUndo, block, pindex, view); | return ApplyBlockUndo(blockUndo, block, pindex, view); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | |||||
static int64_t nTimeTotal = 0; | static int64_t nTimeTotal = 0; | ||||
/** | /** | ||||
* Apply the effects of this block (with given index) on the UTXO set | * Apply the effects of this block (with given index) on the UTXO set | ||||
* represented by coins. Validity checks that depend on the UTXO set are also | * represented by coins. Validity checks that depend on the UTXO set are also | ||||
* done; ConnectBlock() can fail if those validity checks fail (among other | * done; ConnectBlock() can fail if those validity checks fail (among other | ||||
* reasons). | * reasons). | ||||
*/ | */ | ||||
static bool ConnectBlock(const Config &config, const CBlock &block, | bool CChainState::ConnectBlock(const Config &config, const CBlock &block, | ||||
CValidationState &state, CBlockIndex *pindex, | CValidationState &state, CBlockIndex *pindex, | ||||
CCoinsViewCache &view, bool fJustCheck = false) { | CCoinsViewCache &view, bool fJustCheck) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
int64_t nTimeStart = GetTimeMicros(); | int64_t nTimeStart = GetTimeMicros(); | ||||
// Check it again in case a previous version let a bad block in | // Check it again in case a previous version let a bad block in | ||||
BlockValidationOptions validationOptions = | BlockValidationOptions validationOptions = | ||||
BlockValidationOptions(!fJustCheck, !fJustCheck); | BlockValidationOptions(!fJustCheck, !fJustCheck); | ||||
if (!CheckBlock(config, block, state, validationOptions)) { | if (!CheckBlock(config, block, state, validationOptions)) { | ||||
▲ Show 20 Lines • Show All 554 Lines • ▼ Show 20 Lines | |||||
* transactions from disconnected blocks being added to disconnectpool. You | * transactions from disconnected blocks being added to disconnectpool. You | ||||
* should make the mempool consistent again by calling updateMempoolForReorg. | * should make the mempool consistent again by calling updateMempoolForReorg. | ||||
* with cs_main held. | * with cs_main held. | ||||
* | * | ||||
* If disconnectpool is nullptr, then no disconnected transactions are added to | * If disconnectpool is nullptr, then no disconnected transactions are added to | ||||
* disconnectpool (note that the caller is responsible for mempool consistency | * disconnectpool (note that the caller is responsible for mempool consistency | ||||
* in any case). | * in any case). | ||||
*/ | */ | ||||
static bool DisconnectTip(const Config &config, CValidationState &state, | bool CChainState::DisconnectTip(const Config &config, CValidationState &state, | ||||
DisconnectedBlockTransactions *disconnectpool) { | DisconnectedBlockTransactions *disconnectpool) { | ||||
CBlockIndex *pindexDelete = chainActive.Tip(); | CBlockIndex *pindexDelete = chainActive.Tip(); | ||||
assert(pindexDelete); | assert(pindexDelete); | ||||
// Read block from disk. | // Read block from disk. | ||||
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); | ||||
CBlock &block = *pblock; | CBlock &block = *pblock; | ||||
if (!ReadBlockFromDisk(block, pindexDelete, config)) { | if (!ReadBlockFromDisk(block, pindexDelete, config)) { | ||||
return AbortNode(state, "Failed to read block"); | return AbortNode(state, "Failed to read block"); | ||||
▲ Show 20 Lines • Show All 138 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Connect a new block to chainActive. pblock is either nullptr or a pointer to | * Connect a new block to chainActive. pblock is either nullptr or a pointer to | ||||
* a CBlock corresponding to pindexNew, to bypass loading it again from disk. | * a CBlock corresponding to pindexNew, to bypass loading it again from disk. | ||||
* | * | ||||
* The block is always added to connectTrace (either after loading from disk or | * The block is always added to connectTrace (either after loading from disk or | ||||
* by copying pblock) - if that is not intended, care must be taken to remove | * by copying pblock) - if that is not intended, care must be taken to remove | ||||
* the last entry in blocksConnected in case of failure. | * the last entry in blocksConnected in case of failure. | ||||
*/ | */ | ||||
static bool ConnectTip(const Config &config, CValidationState &state, | bool CChainState::ConnectTip(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindexNew, | CBlockIndex *pindexNew, | ||||
const std::shared_ptr<const CBlock> &pblock, | const std::shared_ptr<const CBlock> &pblock, | ||||
ConnectTrace &connectTrace, | ConnectTrace &connectTrace, | ||||
DisconnectedBlockTransactions &disconnectpool) { | DisconnectedBlockTransactions &disconnectpool) { | ||||
assert(pindexNew->pprev == chainActive.Tip()); | assert(pindexNew->pprev == chainActive.Tip()); | ||||
// Read block from disk. | // Read block from disk. | ||||
int64_t nTime1 = GetTimeMicros(); | int64_t nTime1 = GetTimeMicros(); | ||||
std::shared_ptr<const CBlock> pthisBlock; | std::shared_ptr<const CBlock> pthisBlock; | ||||
if (!pblock) { | if (!pblock) { | ||||
std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>(); | std::shared_ptr<CBlock> pblockNew = std::make_shared<CBlock>(); | ||||
if (!ReadBlockFromDisk(*pblockNew, pindexNew, config)) { | if (!ReadBlockFromDisk(*pblockNew, pindexNew, config)) { | ||||
return AbortNode(state, "Failed to read block"); | return AbortNode(state, "Failed to read block"); | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | bool CChainState::ConnectTip(const Config &config, CValidationState &state, | ||||
connectTrace.BlockConnected(pindexNew, std::move(pthisBlock)); | connectTrace.BlockConnected(pindexNew, std::move(pthisBlock)); | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Return the tip of the chain with the most work in it, that isn't known to be | * Return the tip of the chain with the most work in it, that isn't known to be | ||||
* invalid (it's however far from certain to be valid). | * invalid (it's however far from certain to be valid). | ||||
*/ | */ | ||||
static CBlockIndex *FindMostWorkChain() { | CBlockIndex *CChainState::FindMostWorkChain() { | ||||
do { | do { | ||||
CBlockIndex *pindexNew = nullptr; | CBlockIndex *pindexNew = nullptr; | ||||
// Find the best candidate header. | // Find the best candidate header. | ||||
{ | { | ||||
std::set<CBlockIndex *, CBlockIndexWorkComparator>::reverse_iterator | std::set<CBlockIndex *, CBlockIndexWorkComparator>::reverse_iterator | ||||
it = setBlockIndexCandidates.rbegin(); | it = setBlockIndexCandidates.rbegin(); | ||||
if (it == setBlockIndexCandidates.rend()) { | if (it == setBlockIndexCandidates.rend()) { | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | do { | ||||
} | } | ||||
} while (true); | } while (true); | ||||
} | } | ||||
/** | /** | ||||
* Delete all entries in setBlockIndexCandidates that are worse than the current | * Delete all entries in setBlockIndexCandidates that are worse than the current | ||||
* tip. | * tip. | ||||
*/ | */ | ||||
static void PruneBlockIndexCandidates() { | void CChainState::PruneBlockIndexCandidates() { | ||||
// Note that we can't delete the current block itself, as we may need to | // Note that we can't delete the current block itself, as we may need to | ||||
// return to it later in case a reorganization to a better block fails. | // return to it later in case a reorganization to a better block fails. | ||||
auto it = setBlockIndexCandidates.begin(); | auto it = setBlockIndexCandidates.begin(); | ||||
while (it != setBlockIndexCandidates.end() && | while (it != setBlockIndexCandidates.end() && | ||||
setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { | setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { | ||||
setBlockIndexCandidates.erase(it++); | setBlockIndexCandidates.erase(it++); | ||||
} | } | ||||
// Either the current tip or a successor of it we're working towards is left | // Either the current tip or a successor of it we're working towards is left | ||||
// in setBlockIndexCandidates. | // in setBlockIndexCandidates. | ||||
assert(!setBlockIndexCandidates.empty()); | assert(!setBlockIndexCandidates.empty()); | ||||
} | } | ||||
/** | /** | ||||
* Try to make some progress towards making pindexMostWork the active block. | * Try to make some progress towards making pindexMostWork the active block. | ||||
* pblock is either nullptr or a pointer to a CBlock corresponding to | * pblock is either nullptr or a pointer to a CBlock corresponding to | ||||
* pindexMostWork. | * pindexMostWork. | ||||
*/ | */ | ||||
static bool ActivateBestChainStep(const Config &config, CValidationState &state, | bool CChainState::ActivateBestChainStep( | ||||
CBlockIndex *pindexMostWork, | const Config &config, CValidationState &state, CBlockIndex *pindexMostWork, | ||||
const std::shared_ptr<const CBlock> &pblock, | const std::shared_ptr<const CBlock> &pblock, bool &fInvalidFound, | ||||
bool &fInvalidFound, | |||||
ConnectTrace &connectTrace) { | ConnectTrace &connectTrace) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CBlockIndex *pindexOldTip = chainActive.Tip(); | const CBlockIndex *pindexOldTip = chainActive.Tip(); | ||||
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); | const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); | ||||
// Disconnect active blocks which are no longer in the best chain. | // Disconnect active blocks which are no longer in the best chain. | ||||
bool fBlocksDisconnected = false; | bool fBlocksDisconnected = false; | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
while (chainActive.Tip() && chainActive.Tip() != pindexFork) { | while (chainActive.Tip() && chainActive.Tip() != pindexFork) { | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | static void NotifyHeaderTip() { | ||||
} | } | ||||
// Send block tip changed notifications without cs_main | // Send block tip changed notifications without cs_main | ||||
if (fNotify) { | if (fNotify) { | ||||
uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader); | uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader); | ||||
} | } | ||||
} | } | ||||
bool ActivateBestChain(const Config &config, CValidationState &state, | /** | ||||
* Make the best chain active, in multiple steps. The result is either failure | |||||
* or an activated best chain. pblock is either nullptr or a pointer to a block | |||||
* that is already loaded (to avoid loading it again from disk). | |||||
*/ | |||||
bool CChainState::ActivateBestChain(const Config &config, | |||||
CValidationState &state, | |||||
std::shared_ptr<const CBlock> pblock) { | std::shared_ptr<const CBlock> pblock) { | ||||
// Note that while we're often called here from ProcessNewBlock, this is | // Note that while we're often called here from ProcessNewBlock, this is | ||||
// far from a guarantee. Things in the P2P/RPC will often end up calling | // far from a guarantee. Things in the P2P/RPC will often end up calling | ||||
// us in the middle of ProcessNewBlock - do not assume pblock is set | // us in the middle of ProcessNewBlock - do not assume pblock is set | ||||
// sanely for performance or correctness! | // sanely for performance or correctness! | ||||
CBlockIndex *pindexMostWork = nullptr; | CBlockIndex *pindexMostWork = nullptr; | ||||
CBlockIndex *pindexNewTip = nullptr; | CBlockIndex *pindexNewTip = nullptr; | ||||
do { | do { | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | bool CChainState::ActivateBestChain(const Config &config, | ||||
if (nStopAtHeight && pindexNewTip && | if (nStopAtHeight && pindexNewTip && | ||||
pindexNewTip->nHeight >= nStopAtHeight) { | pindexNewTip->nHeight >= nStopAtHeight) { | ||||
StartShutdown(); | StartShutdown(); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool PreciousBlock(const Config &config, CValidationState &state, | bool ActivateBestChain(const Config &config, CValidationState &state, | ||||
std::shared_ptr<const CBlock> pblock) { | |||||
return g_chainstate.ActivateBestChain(config, state, std::move(pblock)); | |||||
} | |||||
bool CChainState::PreciousBlock(const Config &config, CValidationState &state, | |||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (pindex->nChainWork < chainActive.Tip()->nChainWork) { | if (pindex->nChainWork < chainActive.Tip()->nChainWork) { | ||||
// Nothing to do, this block is not at the tip. | // Nothing to do, this block is not at the tip. | ||||
return true; | return true; | ||||
} | } | ||||
if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) { | if (chainActive.Tip()->nChainWork > nLastPreciousChainwork) { | ||||
Show All 15 Lines | bool CChainState::PreciousBlock(const Config &config, CValidationState &state, | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
PruneBlockIndexCandidates(); | PruneBlockIndexCandidates(); | ||||
} | } | ||||
} | } | ||||
return ActivateBestChain(config, state); | return ActivateBestChain(config, state); | ||||
} | } | ||||
static bool UnwindBlock(const Config &config, CValidationState &state, | bool PreciousBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | |||||
return g_chainstate.PreciousBlock(config, state, pindex); | |||||
} | |||||
bool CChainState::UnwindBlock(const Config &config, CValidationState &state, | |||||
CBlockIndex *pindex, bool invalidate) { | CBlockIndex *pindex, bool invalidate) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// Mark the block itself as invalid. | // Mark the block itself as invalid. | ||||
pindex->nStatus = | pindex->nStatus = | ||||
pindex->nStatus.withFailed(invalidate).withParked(!invalidate); | pindex->nStatus.withFailed(invalidate).withParked(!invalidate); | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
Show All 31 Lines | if (invalidate) { | ||||
InvalidChainFound(pindex); | InvalidChainFound(pindex); | ||||
} | } | ||||
uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev); | uiInterface.NotifyBlockTip(IsInitialBlockDownload(), pindex->pprev); | ||||
return true; | return true; | ||||
} | } | ||||
bool InvalidateBlock(const Config &config, CValidationState &state, | bool InvalidateBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
return UnwindBlock(config, state, pindex, true); | return g_chainstate.UnwindBlock(config, state, pindex, true); | ||||
} | } | ||||
/** Park a block. */ | |||||
bool ParkBlock(const Config &config, CValidationState &state, | bool ParkBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
return g_chainstate.ParkBlock(config, state, pindex); | |||||
} | |||||
bool CChainState::ParkBlock(const Config &config, CValidationState &state, | |||||
CBlockIndex *pindex) { | |||||
return UnwindBlock(config, state, pindex, false); | return UnwindBlock(config, state, pindex, false); | ||||
} | } | ||||
template <typename F> bool UpdateFlags(CBlockIndex *pindex, F f) { | template <typename F> bool CChainState::UpdateFlags(CBlockIndex *pindex, F f) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
int nHeight = pindex->nHeight; | int nHeight = pindex->nHeight; | ||||
// Update the flags from this block and all its descendants. | // Update the flags from this block and all its descendants. | ||||
BlockMap::iterator it = mapBlockIndex.begin(); | BlockMap::iterator it = mapBlockIndex.begin(); | ||||
while (it != mapBlockIndex.end()) { | while (it != mapBlockIndex.end()) { | ||||
BlockStatus newStatus = f(it->second->nStatus); | BlockStatus newStatus = f(it->second->nStatus); | ||||
Show All 21 Lines | while (pindex != nullptr) { | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
} | } | ||||
pindex = pindex->pprev; | pindex = pindex->pprev; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool ResetBlockFailureFlags(CBlockIndex *pindex) { | bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
if (pindexBestInvalid && | if (pindexBestInvalid && | ||||
(pindexBestInvalid->GetAncestor(pindex->nHeight) == pindex || | (pindexBestInvalid->GetAncestor(pindex->nHeight) == pindex || | ||||
pindex->GetAncestor(pindexBestInvalid->nHeight) == | pindex->GetAncestor(pindexBestInvalid->nHeight) == | ||||
pindexBestInvalid)) { | pindexBestInvalid)) { | ||||
// Reset the invalid block marker if it is about to be cleared. | // Reset the invalid block marker if it is about to be cleared. | ||||
pindexBestInvalid = nullptr; | pindexBestInvalid = nullptr; | ||||
} | } | ||||
return UpdateFlags(pindex, [](const BlockStatus status) { | return UpdateFlags(pindex, [](const BlockStatus status) { | ||||
return status.withClearedFailureFlags(); | return status.withClearedFailureFlags(); | ||||
}); | }); | ||||
} | } | ||||
bool ResetBlockFailureFlags(CBlockIndex *pindex) { | |||||
return g_chainstate.ResetBlockFailureFlags(pindex); | |||||
} | |||||
/** Remove parked status from a block and its descendants. */ | |||||
bool UnparkBlock(CBlockIndex *pindex) { | bool UnparkBlock(CBlockIndex *pindex) { | ||||
return g_chainstate.UnparkBlock(pindex); | |||||
} | |||||
bool CChainState::UnparkBlock(CBlockIndex *pindex) { | |||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
if (pindexBestParked && | if (pindexBestParked && | ||||
(pindexBestParked->GetAncestor(pindex->nHeight) == pindex || | (pindexBestParked->GetAncestor(pindex->nHeight) == pindex || | ||||
pindex->GetAncestor(pindexBestParked->nHeight) == pindexBestParked)) { | pindex->GetAncestor(pindexBestParked->nHeight) == pindexBestParked)) { | ||||
// Reset the parked block marker if it is about to be cleared. | // Reset the parked block marker if it is about to be cleared. | ||||
pindexBestParked = nullptr; | pindexBestParked = nullptr; | ||||
} | } | ||||
return UpdateFlags(pindex, [](const BlockStatus status) { | return UpdateFlags(pindex, [](const BlockStatus status) { | ||||
return status.withClearedParkedFlags(); | return status.withClearedParkedFlags(); | ||||
}); | }); | ||||
} | } | ||||
static CBlockIndex *AddToBlockIndex(const CBlockHeader &block) { | CBlockIndex *CChainState::AddToBlockIndex(const CBlockHeader &block) { | ||||
// Check for duplicate | // Check for duplicate | ||||
uint256 hash = block.GetHash(); | uint256 hash = block.GetHash(); | ||||
BlockMap::iterator it = mapBlockIndex.find(hash); | BlockMap::iterator it = mapBlockIndex.find(hash); | ||||
if (it != mapBlockIndex.end()) { | if (it != mapBlockIndex.end()) { | ||||
return it->second; | return it->second; | ||||
} | } | ||||
// Construct new block index object | // Construct new block index object | ||||
Show All 29 Lines | CBlockIndex *CChainState::AddToBlockIndex(const CBlockHeader &block) { | ||||
setDirtyBlockIndex.insert(pindexNew); | setDirtyBlockIndex.insert(pindexNew); | ||||
return pindexNew; | return pindexNew; | ||||
} | } | ||||
/** | /** | ||||
* Mark a block as having its data received and checked (up to | * Mark a block as having its data received and checked (up to | ||||
* BLOCK_VALID_TRANSACTIONS). | * BLOCK_VALID_TRANSACTIONS). | ||||
*/ | */ | ||||
bool ReceivedBlockTransactions(const CBlock &block, CValidationState &state, | bool CChainState::ReceivedBlockTransactions(const CBlock &block, | ||||
CValidationState &state, | |||||
CBlockIndex *pindexNew, | CBlockIndex *pindexNew, | ||||
const CDiskBlockPos &pos) { | const CDiskBlockPos &pos) { | ||||
pindexNew->nTx = block.vtx.size(); | pindexNew->nTx = block.vtx.size(); | ||||
pindexNew->nChainTx = 0; | pindexNew->nChainTx = 0; | ||||
pindexNew->nFile = pos.nFile; | pindexNew->nFile = pos.nFile; | ||||
pindexNew->nDataPos = pos.nPos; | pindexNew->nDataPos = pos.nPos; | ||||
pindexNew->nUndoPos = 0; | pindexNew->nUndoPos = 0; | ||||
pindexNew->nStatus = pindexNew->nStatus.withData(); | pindexNew->nStatus = pindexNew->nStatus.withData(); | ||||
pindexNew->RaiseValidity(BlockValidity::TRANSACTIONS); | pindexNew->RaiseValidity(BlockValidity::TRANSACTIONS); | ||||
setDirtyBlockIndex.insert(pindexNew); | setDirtyBlockIndex.insert(pindexNew); | ||||
▲ Show 20 Lines • Show All 464 Lines • ▼ Show 20 Lines | static bool ContextualCheckBlock(const Config &config, const CBlock &block, | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* If the provided block header is valid, add it to the block index. | * If the provided block header is valid, add it to the block index. | ||||
* | * | ||||
* Returns true if the block is succesfully added to the block index. | * Returns true if the block is succesfully added to the block index. | ||||
*/ | */ | ||||
static bool AcceptBlockHeader(const Config &config, const CBlockHeader &block, | bool CChainState::AcceptBlockHeader(const Config &config, | ||||
CValidationState &state, CBlockIndex **ppindex) { | const CBlockHeader &block, | ||||
CValidationState &state, | |||||
CBlockIndex **ppindex) { | |||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
// Check for duplicate | // Check for duplicate | ||||
uint256 hash = block.GetHash(); | uint256 hash = block.GetHash(); | ||||
BlockMap::iterator miSelf = mapBlockIndex.find(hash); | BlockMap::iterator miSelf = mapBlockIndex.find(hash); | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (hash != chainparams.GetConsensus().hashGenesisBlock) { | if (hash != chainparams.GetConsensus().hashGenesisBlock) { | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | if (first_invalid != nullptr) { | ||||
first_invalid->SetNull(); | first_invalid->SetNull(); | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
for (const CBlockHeader &header : headers) { | for (const CBlockHeader &header : headers) { | ||||
// Use a temp pindex instead of ppindex to avoid a const_cast | // Use a temp pindex instead of ppindex to avoid a const_cast | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (!AcceptBlockHeader(config, header, state, &pindex)) { | if (!g_chainstate.AcceptBlockHeader(config, header, state, | ||||
&pindex)) { | |||||
if (first_invalid) { | if (first_invalid) { | ||||
*first_invalid = header; | *first_invalid = header; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
if (ppindex) { | if (ppindex) { | ||||
*ppindex = pindex; | *ppindex = pindex; | ||||
Show All 37 Lines | |||||
* @param[out] ppindex The last new block index, only set if the block | * @param[out] ppindex The last new block index, only set if the block | ||||
* was accepted. | * was accepted. | ||||
* @param[in] fRequested A boolean to indicate if this block was requested | * @param[in] fRequested A boolean to indicate if this block was requested | ||||
* from our peers. | * from our peers. | ||||
* @param[in] dbp If non-null, the disk position of the block. | * @param[in] dbp If non-null, the disk position of the block. | ||||
* @param[in-out] fNewBlock True if block was first received via this call. | * @param[in-out] fNewBlock True if block was first received via this call. | ||||
* @return True if the block is accepted as a valid block and written to disk. | * @return True if the block is accepted as a valid block and written to disk. | ||||
*/ | */ | ||||
static bool AcceptBlock(const Config &config, | bool CChainState::AcceptBlock(const Config &config, | ||||
const std::shared_ptr<const CBlock> &pblock, | const std::shared_ptr<const CBlock> &pblock, | ||||
CValidationState &state, CBlockIndex **ppindex, | CValidationState &state, CBlockIndex **ppindex, | ||||
bool fRequested, const CDiskBlockPos *dbp, | bool fRequested, const CDiskBlockPos *dbp, | ||||
bool *fNewBlock) { | bool *fNewBlock) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const CBlock &block = *pblock; | const CBlock &block = *pblock; | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
*fNewBlock = false; | *fNewBlock = false; | ||||
} | } | ||||
CBlockIndex *pindexDummy = nullptr; | CBlockIndex *pindexDummy = nullptr; | ||||
CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy; | CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy; | ||||
if (!AcceptBlockHeader(config, block, state, &pindex)) { | if (!g_chainstate.AcceptBlockHeader(config, block, state, &pindex)) { | ||||
return false; | return false; | ||||
} | } | ||||
// Try to process all requested blocks that we don't have, but only | // Try to process all requested blocks that we don't have, but only | ||||
// process an unrequested block if it's new and has enough work to | // process an unrequested block if it's new and has enough work to | ||||
// advance our tip, and isn't too many blocks ahead. | // advance our tip, and isn't too many blocks ahead. | ||||
bool fAlreadyHave = pindex->nStatus.hasData(); | bool fAlreadyHave = pindex->nStatus.hasData(); | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | try { | ||||
return AbortNode(state, std::string("System error: ") + e.what()); | return AbortNode(state, std::string("System error: ") + e.what()); | ||||
} | } | ||||
if (fCheckForPruning) { | if (fCheckForPruning) { | ||||
// we just allocated more disk space for block files. | // we just allocated more disk space for block files. | ||||
FlushStateToDisk(config.GetChainParams(), state, FLUSH_STATE_NONE); | FlushStateToDisk(config.GetChainParams(), state, FLUSH_STATE_NONE); | ||||
} | } | ||||
CheckBlockIndex(chainparams.GetConsensus()); | |||||
return true; | return true; | ||||
} | } | ||||
bool ProcessNewBlock(const Config &config, | bool ProcessNewBlock(const Config &config, | ||||
const std::shared_ptr<const CBlock> pblock, | const std::shared_ptr<const CBlock> pblock, | ||||
bool fForceProcessing, bool *fNewBlock) { | bool fForceProcessing, bool *fNewBlock) { | ||||
{ | { | ||||
CBlockIndex *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
if (fNewBlock) { | if (fNewBlock) { | ||||
*fNewBlock = false; | *fNewBlock = false; | ||||
} | } | ||||
const CChainParams &chainparams = config.GetChainParams(); | |||||
CValidationState state; | CValidationState state; | ||||
// Ensure that CheckBlock() passes before calling AcceptBlock, as | // Ensure that CheckBlock() passes before calling AcceptBlock, as | ||||
// belt-and-suspenders. | // belt-and-suspenders. | ||||
bool ret = CheckBlock(config, *pblock, state); | bool ret = CheckBlock(config, *pblock, state); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (ret) { | if (ret) { | ||||
// Store to disk | // Store to disk | ||||
ret = AcceptBlock(config, pblock, state, &pindex, fForceProcessing, | ret = | ||||
nullptr, fNewBlock); | g_chainstate.AcceptBlock(config, pblock, state, &pindex, | ||||
fForceProcessing, nullptr, fNewBlock); | |||||
} | } | ||||
CheckBlockIndex(chainparams.GetConsensus()); | |||||
if (!ret) { | if (!ret) { | ||||
GetMainSignals().BlockChecked(*pblock, state); | GetMainSignals().BlockChecked(*pblock, state); | ||||
return error("%s: AcceptBlock FAILED", __func__); | return error("%s: AcceptBlock FAILED", __func__); | ||||
} | } | ||||
} | } | ||||
NotifyHeaderTip(); | NotifyHeaderTip(); | ||||
// Only used to report errors, not invalidity - ignore it | // Only used to report errors, not invalidity - ignore it | ||||
CValidationState state; | CValidationState state; | ||||
if (!ActivateBestChain(config, state, pblock)) { | if (!g_chainstate.ActivateBestChain(config, state, pblock)) { | ||||
return error("%s: ActivateBestChain failed", __func__); | return error("%s: ActivateBestChain failed", __func__); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool TestBlockValidity(const Config &config, CValidationState &state, | bool TestBlockValidity(const Config &config, CValidationState &state, | ||||
const CBlock &block, CBlockIndex *pindexPrev, | const CBlock &block, CBlockIndex *pindexPrev, | ||||
Show All 26 Lines | if (!CheckBlock(config, block, state, validationOptions)) { | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
if (!ContextualCheckBlock(config, block, state, pindexPrev)) { | if (!ContextualCheckBlock(config, block, state, pindexPrev)) { | ||||
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, | return error("%s: Consensus::ContextualCheckBlock: %s", __func__, | ||||
FormatStateMessage(state)); | FormatStateMessage(state)); | ||||
} | } | ||||
if (!ConnectBlock(config, block, state, &indexDummy, viewNew, true)) { | if (!g_chainstate.ConnectBlock(config, block, state, &indexDummy, viewNew, | ||||
true)) { | |||||
return false; | return false; | ||||
} | } | ||||
assert(state.IsValid()); | assert(state.IsValid()); | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 220 Lines • ▼ Show 20 Lines | |||||
static FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { | static FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { | ||||
return OpenDiskFile(pos, "rev", fReadOnly); | return OpenDiskFile(pos, "rev", fReadOnly); | ||||
} | } | ||||
fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) { | fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) { | ||||
return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); | return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); | ||||
} | } | ||||
CBlockIndex *InsertBlockIndex(uint256 hash) { | CBlockIndex *CChainState::InsertBlockIndex(const uint256 &hash) { | ||||
if (hash.IsNull()) { | if (hash.IsNull()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// Return existing | // Return existing | ||||
BlockMap::iterator mi = mapBlockIndex.find(hash); | BlockMap::iterator mi = mapBlockIndex.find(hash); | ||||
if (mi != mapBlockIndex.end()) { | if (mi != mapBlockIndex.end()) { | ||||
return (*mi).second; | return (*mi).second; | ||||
} | } | ||||
// Create new | // Create new | ||||
CBlockIndex *pindexNew = new CBlockIndex(); | CBlockIndex *pindexNew = new CBlockIndex(); | ||||
if (!pindexNew) { | if (!pindexNew) { | ||||
throw std::runtime_error(std::string(__func__) + | throw std::runtime_error(std::string(__func__) + | ||||
": new CBlockIndex failed"); | ": new CBlockIndex failed"); | ||||
} | } | ||||
mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; | mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first; | ||||
pindexNew->phashBlock = &((*mi).first); | pindexNew->phashBlock = &((*mi).first); | ||||
return pindexNew; | return pindexNew; | ||||
} | } | ||||
static bool LoadBlockIndexDB(const Config &config) { | bool CChainState::LoadBlockIndex(const Config &config, | ||||
if (!pblocktree->LoadBlockIndexGuts(config, InsertBlockIndex)) { | CBlockTreeDB &blocktree) { | ||||
if (!blocktree.LoadBlockIndexGuts(config, [this](const uint256 &hash) { | |||||
return this->InsertBlockIndex(hash); | |||||
})) { | |||||
return false; | return false; | ||||
} | } | ||||
boost::this_thread::interruption_point(); | boost::this_thread::interruption_point(); | ||||
// Calculate nChainWork | // Calculate nChainWork | ||||
std::vector<std::pair<int, CBlockIndex *>> vSortedByHeight; | std::vector<std::pair<int, CBlockIndex *>> vSortedByHeight; | ||||
vSortedByHeight.reserve(mapBlockIndex.size()); | vSortedByHeight.reserve(mapBlockIndex.size()); | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | for (const std::pair<int, CBlockIndex *> &item : vSortedByHeight) { | ||||
if (pindex->IsValid(BlockValidity::TREE) && | if (pindex->IsValid(BlockValidity::TREE) && | ||||
(pindexBestHeader == nullptr || | (pindexBestHeader == nullptr || | ||||
CBlockIndexWorkComparator()(pindexBestHeader, pindex))) { | CBlockIndexWorkComparator()(pindexBestHeader, pindex))) { | ||||
pindexBestHeader = pindex; | pindexBestHeader = pindex; | ||||
} | } | ||||
} | } | ||||
return true; | |||||
} | |||||
bool static LoadBlockIndexDB(const Config &config) { | |||||
if (!g_chainstate.LoadBlockIndex(config, *pblocktree)) return false; | |||||
// Load block file info | // Load block file info | ||||
pblocktree->ReadLastBlockFile(nLastBlockFile); | pblocktree->ReadLastBlockFile(nLastBlockFile); | ||||
vinfoBlockFile.resize(nLastBlockFile + 1); | vinfoBlockFile.resize(nLastBlockFile + 1); | ||||
LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); | LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); | ||||
for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { | for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { | ||||
pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); | pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); | ||||
} | } | ||||
Show All 32 Lines | bool static LoadBlockIndexDB(const Config &config) { | ||||
if (fHavePruned) { | if (fHavePruned) { | ||||
LogPrintf( | LogPrintf( | ||||
"LoadBlockIndexDB(): Block files have previously been pruned\n"); | "LoadBlockIndexDB(): Block files have previously been pruned\n"); | ||||
} | } | ||||
// Check whether we need to continue reindexing | // Check whether we need to continue reindexing | ||||
bool fReindexing = false; | bool fReindexing = false; | ||||
pblocktree->ReadReindexing(fReindexing); | pblocktree->ReadReindexing(fReindexing); | ||||
fReindex |= fReindexing; | fReindex = (fReindex | fReindexing); | ||||
// Check whether we have a transaction index | // Check whether we have a transaction index | ||||
pblocktree->ReadFlag("txindex", fTxIndex); | pblocktree->ReadFlag("txindex", fTxIndex); | ||||
LogPrintf("%s: transaction index %s\n", __func__, | LogPrintf("%s: transaction index %s\n", __func__, | ||||
fTxIndex ? "enabled" : "disabled"); | fTxIndex ? "enabled" : "disabled"); | ||||
return true; | return true; | ||||
} | } | ||||
Show All 17 Lines | bool LoadChainTip(const Config &config) { | ||||
// Load pointer to end of best chain | // Load pointer to end of best chain | ||||
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); | BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); | ||||
if (it == mapBlockIndex.end()) { | if (it == mapBlockIndex.end()) { | ||||
return false; | return false; | ||||
} | } | ||||
chainActive.SetTip(it->second); | chainActive.SetTip(it->second); | ||||
PruneBlockIndexCandidates(); | g_chainstate.PruneBlockIndexCandidates(); | ||||
LogPrintf( | LogPrintf( | ||||
"Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", | "Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", | ||||
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), | chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), | ||||
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", | DateTimeStrFormat("%Y-%m-%d %H:%M:%S", | ||||
chainActive.Tip()->GetBlockTime()), | chainActive.Tip()->GetBlockTime()), | ||||
GuessVerificationProgress(config.GetChainParams().TxData(), | GuessVerificationProgress(config.GetChainParams().TxData(), | ||||
chainActive.Tip())); | chainActive.Tip())); | ||||
▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | for (CBlockIndex *pindex = chainActive.Tip(); pindex && pindex->pprev; | ||||
} | } | ||||
// check level 3: check for inconsistencies during memory-only | // check level 3: check for inconsistencies during memory-only | ||||
// disconnect of tip blocks | // disconnect of tip blocks | ||||
if (nCheckLevel >= 3 && pindex == pindexState && | if (nCheckLevel >= 3 && pindex == pindexState && | ||||
(coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= | (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= | ||||
nCoinCacheUsage) { | nCoinCacheUsage) { | ||||
assert(coins.GetBestBlock() == pindex->GetBlockHash()); | assert(coins.GetBestBlock() == pindex->GetBlockHash()); | ||||
DisconnectResult res = DisconnectBlock(block, pindex, coins); | DisconnectResult res = | ||||
g_chainstate.DisconnectBlock(block, pindex, coins); | |||||
if (res == DISCONNECT_FAILED) { | if (res == DISCONNECT_FAILED) { | ||||
return error("VerifyDB(): *** irrecoverable inconsistency in " | return error("VerifyDB(): *** irrecoverable inconsistency in " | ||||
"block data at %d, hash=%s", | "block data at %d, hash=%s", | ||||
pindex->nHeight, | pindex->nHeight, | ||||
pindex->GetBlockHash().ToString()); | pindex->GetBlockHash().ToString()); | ||||
} | } | ||||
pindexState = pindex->pprev; | pindexState = pindex->pprev; | ||||
Show All 31 Lines | if (nCheckLevel >= 4) { | ||||
(double)nCheckDepth * 50)))); | (double)nCheckDepth * 50)))); | ||||
pindex = chainActive.Next(pindex); | pindex = chainActive.Next(pindex); | ||||
CBlock block; | CBlock block; | ||||
if (!ReadBlockFromDisk(block, pindex, config)) { | if (!ReadBlockFromDisk(block, pindex, config)) { | ||||
return error( | return error( | ||||
"VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", | "VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", | ||||
pindex->nHeight, pindex->GetBlockHash().ToString()); | pindex->nHeight, pindex->GetBlockHash().ToString()); | ||||
} | } | ||||
if (!ConnectBlock(config, block, state, pindex, coins)) { | if (!g_chainstate.ConnectBlock(config, block, state, pindex, | ||||
coins)) { | |||||
return error( | return error( | ||||
"VerifyDB(): *** found unconnectable block at %d, hash=%s", | "VerifyDB(): *** found unconnectable block at %d, hash=%s", | ||||
pindex->nHeight, pindex->GetBlockHash().ToString()); | pindex->nHeight, pindex->GetBlockHash().ToString()); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
LogPrintf("[DONE].\n"); | LogPrintf("[DONE].\n"); | ||||
LogPrintf("No coin database inconsistencies in last %i blocks (%i " | LogPrintf("No coin database inconsistencies in last %i blocks (%i " | ||||
"transactions)\n", | "transactions)\n", | ||||
chainActive.Height() - pindexState->nHeight, nGoodTransactions); | chainActive.Height() - pindexState->nHeight, nGoodTransactions); | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Apply the effects of a block on the utxo cache, ignoring that it may already | * Apply the effects of a block on the utxo cache, ignoring that it may already | ||||
* have been applied. | * have been applied. | ||||
*/ | */ | ||||
static bool RollforwardBlock(const CBlockIndex *pindex, CCoinsViewCache &view, | bool CChainState::RollforwardBlock(const CBlockIndex *pindex, | ||||
CCoinsViewCache &view, | |||||
const Config &config) { | const Config &config) { | ||||
// TODO: merge with ConnectBlock | // TODO: merge with ConnectBlock | ||||
CBlock block; | CBlock block; | ||||
if (!ReadBlockFromDisk(block, pindex, config)) { | if (!ReadBlockFromDisk(block, pindex, config)) { | ||||
return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", | return error("ReplayBlock(): ReadBlockFromDisk failed at %d, hash=%s", | ||||
pindex->nHeight, pindex->GetBlockHash().ToString()); | pindex->nHeight, pindex->GetBlockHash().ToString()); | ||||
} | } | ||||
for (const CTransactionRef &tx : block.vtx) { | for (const CTransactionRef &tx : block.vtx) { | ||||
Show All 9 Lines | for (const CTransactionRef &tx : block.vtx) { | ||||
for (const CTxIn &txin : tx->vin) { | for (const CTxIn &txin : tx->vin) { | ||||
view.SpendCoin(txin.prevout); | view.SpendCoin(txin.prevout); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool ReplayBlocks(const Config &config, CCoinsView *view) { | bool CChainState::ReplayBlocks(const Config &config, CCoinsView *view) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CCoinsViewCache cache(view); | CCoinsViewCache cache(view); | ||||
std::vector<uint256> hashHeads = view->GetHeadBlocks(); | std::vector<uint256> hashHeads = view->GetHeadBlocks(); | ||||
if (hashHeads.empty()) { | if (hashHeads.empty()) { | ||||
// We're already in a consistent state. | // We're already in a consistent state. | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | bool CChainState::ReplayBlocks(const Config &config, CCoinsView *view) { | ||||
} | } | ||||
cache.SetBestBlock(pindexNew->GetBlockHash()); | cache.SetBestBlock(pindexNew->GetBlockHash()); | ||||
cache.Flush(); | cache.Flush(); | ||||
uiInterface.ShowProgress("", 100); | uiInterface.ShowProgress("", 100); | ||||
return true; | return true; | ||||
} | } | ||||
bool RewindBlockIndex(const Config &config) { | bool ReplayBlocks(const Config &config, CCoinsView *view) { | ||||
return g_chainstate.ReplayBlocks(config, view); | |||||
} | |||||
bool CChainState::RewindBlockIndex(const Config &config) { | |||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CChainParams ¶ms = config.GetChainParams(); | const CChainParams ¶ms = config.GetChainParams(); | ||||
int nHeight = chainActive.Height() + 1; | int nHeight = chainActive.Height() + 1; | ||||
// nHeight is now the height of the first insufficiently-validated block, or | // nHeight is now the height of the first insufficiently-validated block, or | ||||
// tipheight + 1 | // tipheight + 1 | ||||
CValidationState state; | CValidationState state; | ||||
Show All 32 Lines | bool CChainState::RewindBlockIndex(const Config &config) { | ||||
} | } | ||||
if (chainActive.Tip() != nullptr) { | if (chainActive.Tip() != nullptr) { | ||||
// We can't prune block index candidates based on our tip if we have | // We can't prune block index candidates based on our tip if we have | ||||
// no tip due to chainActive being empty! | // no tip due to chainActive being empty! | ||||
PruneBlockIndexCandidates(); | PruneBlockIndexCandidates(); | ||||
CheckBlockIndex(params.GetConsensus()); | CheckBlockIndex(params.GetConsensus()); | ||||
} | |||||
return true; | |||||
} | |||||
bool RewindBlockIndex(const Config &config) { | |||||
if (!g_chainstate.RewindBlockIndex(config)) { | |||||
return false; | |||||
} | |||||
if (chainActive.Tip() != nullptr) { | |||||
// FlushStateToDisk can possibly read chainActive. Be conservative | // FlushStateToDisk can possibly read chainActive. Be conservative | ||||
// and skip it here, we're about to -reindex-chainstate anyway, so | // and skip it here, we're about to -reindex-chainstate anyway, so | ||||
// it'll get called a bunch real soon. | // it'll get called a bunch real soon. | ||||
if (!FlushStateToDisk(params, state, FLUSH_STATE_ALWAYS)) { | CValidationState state; | ||||
if (!FlushStateToDisk(config.GetChainParams(), state, | |||||
FLUSH_STATE_ALWAYS)) { | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
// May NOT be used after any connections are up as much of the peer-processing | // May NOT be used after any connections are up as much of the peer-processing | ||||
// logic assumes a consistent block index state | // logic assumes a consistent block index state | ||||
void CChainState::UnloadBlockIndex() { | |||||
setBlockIndexCandidates.clear(); | |||||
} | |||||
// May NOT be used after any connections are up as much | |||||
// of the peer-processing logic assumes a consistent | |||||
// block index state | |||||
void UnloadBlockIndex() { | void UnloadBlockIndex() { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
setBlockIndexCandidates.clear(); | |||||
chainActive.SetTip(nullptr); | chainActive.SetTip(nullptr); | ||||
pindexBestInvalid = nullptr; | pindexBestInvalid = nullptr; | ||||
pindexBestParked = nullptr; | pindexBestParked = nullptr; | ||||
pindexBestHeader = nullptr; | pindexBestHeader = nullptr; | ||||
mempool.clear(); | mempool.clear(); | ||||
mapBlocksUnlinked.clear(); | mapBlocksUnlinked.clear(); | ||||
vinfoBlockFile.clear(); | vinfoBlockFile.clear(); | ||||
nLastBlockFile = 0; | nLastBlockFile = 0; | ||||
nBlockSequenceId = 1; | nBlockSequenceId = 1; | ||||
setDirtyBlockIndex.clear(); | setDirtyBlockIndex.clear(); | ||||
setDirtyFileInfo.clear(); | setDirtyFileInfo.clear(); | ||||
versionbitscache.Clear(); | versionbitscache.Clear(); | ||||
for (BlockMap::value_type &entry : mapBlockIndex) { | for (BlockMap::value_type &entry : mapBlockIndex) { | ||||
delete entry.second; | delete entry.second; | ||||
} | } | ||||
mapBlockIndex.clear(); | mapBlockIndex.clear(); | ||||
fHavePruned = false; | fHavePruned = false; | ||||
g_chainstate.UnloadBlockIndex(); | |||||
} | } | ||||
bool LoadBlockIndex(const Config &config) { | bool LoadBlockIndex(const Config &config) { | ||||
// Load block index from databases | // Load block index from databases | ||||
bool needs_init = fReindex; | bool needs_init = fReindex; | ||||
if (!fReindex) { | if (!fReindex) { | ||||
bool ret = LoadBlockIndexDB(config); | bool ret = LoadBlockIndexDB(config); | ||||
if (!ret) { | if (!ret) { | ||||
Show All 13 Lines | if (needs_init) { | ||||
LogPrintf("Initializing databases...\n"); | LogPrintf("Initializing databases...\n"); | ||||
// Use the provided setting for -txindex in the new database | // Use the provided setting for -txindex in the new database | ||||
fTxIndex = gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX); | fTxIndex = gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX); | ||||
pblocktree->WriteFlag("txindex", fTxIndex); | pblocktree->WriteFlag("txindex", fTxIndex); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool LoadGenesisBlock(const CChainParams &chainparams) { | bool CChainState::LoadGenesisBlock(const CChainParams &chainparams) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// Check whether we're already initialized by checking for genesis in | // Check whether we're already initialized by checking for genesis in | ||||
// mapBlockIndex. Note that we can't use chainActive here, since it is | // mapBlockIndex. Note that we can't use chainActive here, since it is | ||||
// set based on the coins db, not the block index db, which is the only | // set based on the coins db, not the block index db, which is the only | ||||
// thing loaded at this point. | // thing loaded at this point. | ||||
if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash())) { | if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash())) { | ||||
return true; | return true; | ||||
Show All 16 Lines | bool CChainState::LoadGenesisBlock(const CChainParams &chainparams) { | ||||
} catch (const std::runtime_error &e) { | } catch (const std::runtime_error &e) { | ||||
return error("%s: failed to write genesis block: %s", __func__, | return error("%s: failed to write genesis block: %s", __func__, | ||||
e.what()); | e.what()); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool LoadGenesisBlock(const CChainParams &chainparams) { | |||||
return g_chainstate.LoadGenesisBlock(chainparams); | |||||
} | |||||
bool LoadExternalBlockFile(const Config &config, FILE *fileIn, | bool LoadExternalBlockFile(const Config &config, FILE *fileIn, | ||||
CDiskBlockPos *dbp) { | CDiskBlockPos *dbp) { | ||||
// Map of disk positions for blocks with unknown parent (only used for | // Map of disk positions for blocks with unknown parent (only used for | ||||
// reindex) | // reindex) | ||||
static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent; | static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent; | ||||
int64_t nStart = GetTimeMillis(); | int64_t nStart = GetTimeMillis(); | ||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | try { | ||||
continue; | continue; | ||||
} | } | ||||
// process in case the block isn't known yet | // process in case the block isn't known yet | ||||
if (mapBlockIndex.count(hash) == 0 || | if (mapBlockIndex.count(hash) == 0 || | ||||
!mapBlockIndex[hash]->nStatus.hasData()) { | !mapBlockIndex[hash]->nStatus.hasData()) { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CValidationState state; | CValidationState state; | ||||
if (AcceptBlock(config, pblock, state, nullptr, true, dbp, | if (g_chainstate.AcceptBlock(config, pblock, state, nullptr, | ||||
nullptr)) { | true, dbp, nullptr)) { | ||||
nLoaded++; | nLoaded++; | ||||
} | } | ||||
if (state.IsError()) { | if (state.IsError()) { | ||||
break; | break; | ||||
} | } | ||||
} else if (hash != | } else if (hash != | ||||
chainparams.GetConsensus().hashGenesisBlock && | chainparams.GetConsensus().hashGenesisBlock && | ||||
mapBlockIndex[hash]->nHeight % 1000 == 0) { | mapBlockIndex[hash]->nHeight % 1000 == 0) { | ||||
LogPrint( | LogPrint( | ||||
BCLog::REINDEX, | BCLog::REINDEX, | ||||
"Block Import: already had block %s at height %d\n", | "Block Import: already had block %s at height %d\n", | ||||
hash.ToString(), mapBlockIndex[hash]->nHeight); | hash.ToString(), mapBlockIndex[hash]->nHeight); | ||||
} | } | ||||
// Activate the genesis block so normal node progress can | // Activate the genesis block so normal node progress can | ||||
// continue | // continue | ||||
if (hash == chainparams.GetConsensus().hashGenesisBlock) { | if (hash == chainparams.GetConsensus().hashGenesisBlock) { | ||||
CValidationState state; | CValidationState state; | ||||
if (!ActivateBestChain(config, state)) { | if (!g_chainstate.ActivateBestChain(config, state)) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
NotifyHeaderTip(); | NotifyHeaderTip(); | ||||
// Recursively process earlier encountered successors of this | // Recursively process earlier encountered successors of this | ||||
// block | // block | ||||
Show All 14 Lines | try { | ||||
config)) { | config)) { | ||||
LogPrint( | LogPrint( | ||||
BCLog::REINDEX, | BCLog::REINDEX, | ||||
"%s: Processing out of order child %s of %s\n", | "%s: Processing out of order child %s of %s\n", | ||||
__func__, pblockrecursive->GetHash().ToString(), | __func__, pblockrecursive->GetHash().ToString(), | ||||
head.ToString()); | head.ToString()); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CValidationState dummy; | CValidationState dummy; | ||||
if (AcceptBlock(config, pblockrecursive, dummy, | if (g_chainstate.AcceptBlock( | ||||
nullptr, true, &it->second, | config, pblockrecursive, dummy, nullptr, | ||||
nullptr)) { | true, &it->second, nullptr)) { | ||||
nLoaded++; | nLoaded++; | ||||
queue.push_back(pblockrecursive->GetHash()); | queue.push_back(pblockrecursive->GetHash()); | ||||
} | } | ||||
} | } | ||||
range.first++; | range.first++; | ||||
mapBlocksUnknownParent.erase(it); | mapBlocksUnknownParent.erase(it); | ||||
NotifyHeaderTip(); | NotifyHeaderTip(); | ||||
} | } | ||||
Show All 10 Lines | bool LoadExternalBlockFile(const Config &config, FILE *fileIn, | ||||
if (nLoaded > 0) { | if (nLoaded > 0) { | ||||
LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, | LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, | ||||
GetTimeMillis() - nStart); | GetTimeMillis() - nStart); | ||||
} | } | ||||
return nLoaded > 0; | return nLoaded > 0; | ||||
} | } | ||||
static void CheckBlockIndex(const Consensus::Params &consensusParams) { | void CChainState::CheckBlockIndex(const Consensus::Params &consensusParams) { | ||||
if (!fCheckBlockIndex) { | if (!fCheckBlockIndex) { | ||||
return; | return; | ||||
} | } | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
// During a reindex, we read the genesis block and call CheckBlockIndex | // During a reindex, we read the genesis block and call CheckBlockIndex | ||||
// before ActivateBestChain, so we have the genesis block in mapBlockIndex | // before ActivateBestChain, so we have the genesis block in mapBlockIndex | ||||
▲ Show 20 Lines • Show All 480 Lines • Show Last 20 Lines |