Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
#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 | ||||
#define MICRO 0.000001 | #define MICRO 0.000001 | ||||
#define MILLI 0.001 | #define MILLI 0.001 | ||||
class ConnectTrace; | |||||
/** | |||||
* 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). | |||||
*/ | |||||
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; | |||||
public: | |||||
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, 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> | |||||
void UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f); | |||||
template <typename F, typename C> | |||||
void UpdateFlags(CBlockIndex *pindex, F f, C fchild); | |||||
template <typename F> void UpdateFlags(CBlockIndex *pindex, F f); | |||||
/** Remove parked status from a block and its descendants. */ | |||||
bool UnparkBlockImpl(CBlockIndex *pindex, bool fClearChildren); | |||||
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; | |||||
/** | /** | ||||
* Global state | * Global state | ||||
*/ | */ | ||||
CCriticalSection cs_main; | CCriticalSection cs_main; | ||||
BlockMap mapBlockIndex; | BlockMap &mapBlockIndex = g_chainstate.mapBlockIndex; | ||||
CChain chainActive; | 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); | ||||
std::atomic_bool fReindex(false); | std::atomic_bool fReindex(false); | ||||
bool fTxIndex = false; | bool fTxIndex = false; | ||||
Show All 9 Lines | |||||
uint256 hashAssumeValid; | uint256 hashAssumeValid; | ||||
arith_uint256 nMinimumChainWork; | arith_uint256 nMinimumChainWork; | ||||
Amount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; | Amount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; | ||||
CTxMemPool g_mempool; | CTxMemPool g_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 *pindexBestInvalid; | CBlockIndex *&pindexBestParked = g_chainstate.pindexBestParked; | ||||
CBlockIndex *pindexBestParked; | |||||
/** | /** | ||||
* The best finalized block. | * The best finalized block. | ||||
* This block cannot be reorged in any way, shape or form. | * This block cannot be reorged in any way, shape or form. | ||||
*/ | */ | ||||
CBlockIndex const *pindexFinalized; | CBlockIndex const *pindexFinalized; | ||||
/** | /** | ||||
* 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 | * 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. | * 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 block/undo files | * Global flag to indicate we should check to see if there are block/undo files | ||||
* that should be deleted. Set on startup or if we allocate more file space when | * that should be deleted. Set on startup or if we allocate more file space when | ||||
* we're in prune mode. | * we're in prune mode. | ||||
▲ Show 20 Lines • Show All 944 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), | ||||
FormatISO8601DateTime(tip->GetBlockTime())); | FormatISO8601DateTime(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 290 Lines • ▼ Show 20 Lines | DisconnectResult UndoCoinSpend(const Coin &undo, CCoinsViewCache &view, | ||||
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 231 Lines • ▼ Show 20 Lines | |||||
static int64_t nBlocksTotal = 0; | static int64_t nBlocksTotal = 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); | ||||
assert(pindex); | assert(pindex); | ||||
// pindex->phashBlock can be null if called by | // pindex->phashBlock can be null if called by | ||||
// CreateNewBlock/TestBlockValidity | // CreateNewBlock/TestBlockValidity | ||||
assert((pindex->phashBlock == nullptr) || | assert((pindex->phashBlock == nullptr) || | ||||
(*pindex->phashBlock == block.GetHash())); | (*pindex->phashBlock == block.GetHash())); | ||||
int64_t nTimeStart = GetTimeMicros(); | int64_t nTimeStart = GetTimeMicros(); | ||||
▲ Show 20 Lines • Show All 549 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 212 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) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
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>(); | ||||
▲ Show 20 Lines • Show All 99 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() { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
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(); | ||||
▲ Show 20 Lines • Show All 132 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 101 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, | 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! | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
CBlockIndex *pindexMostWork = nullptr; | CBlockIndex *pindexMostWork = nullptr; | ||||
CBlockIndex *pindexNewTip = nullptr; | CBlockIndex *pindexNewTip = nullptr; | ||||
▲ Show 20 Lines • Show All 86 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 19 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 as either invalid or parked. | // Mark the block as either invalid or parked. | ||||
pindex->nStatus = invalidate ? pindex->nStatus.withFailed() | pindex->nStatus = invalidate ? pindex->nStatus.withFailed() | ||||
: pindex->nStatus.withParked(); | : pindex->nStatus.withParked(); | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
DisconnectedBlockTransactions disconnectpool; | DisconnectedBlockTransactions disconnectpool; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if (!AreOnTheSameFork(pindex, chainActive.Tip())) { | ||||
return InvalidateBlock(config, state, pindexToInvalidate); | return InvalidateBlock(config, state, pindexToInvalidate); | ||||
} | } | ||||
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); | ||||
} | } | ||||
bool ParkBlock(const Config &config, CValidationState &state, | bool ParkBlock(const Config &config, CValidationState &state, | ||||
CBlockIndex *pindex) { | CBlockIndex *pindex) { | ||||
return UnwindBlock(config, state, pindex, false); | return g_chainstate.UnwindBlock(config, state, pindex, false); | ||||
} | } | ||||
template <typename F> | template <typename F> | ||||
void UpdateFlagsForBlock(CBlockIndex *pindexBase, CBlockIndex *pindex, F f) { | void CChainState::UpdateFlagsForBlock(CBlockIndex *pindexBase, | ||||
CBlockIndex *pindex, F f) { | |||||
BlockStatus newStatus = f(pindex->nStatus); | BlockStatus newStatus = f(pindex->nStatus); | ||||
if (pindex->nStatus != newStatus && | if (pindex->nStatus != newStatus && | ||||
pindex->GetAncestor(pindexBase->nHeight) == pindexBase) { | pindex->GetAncestor(pindexBase->nHeight) == pindexBase) { | ||||
pindex->nStatus = newStatus; | pindex->nStatus = newStatus; | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx && | if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx && | ||||
setBlockIndexCandidates.value_comp()(chainActive.Tip(), pindex)) { | setBlockIndexCandidates.value_comp()(chainActive.Tip(), pindex)) { | ||||
setBlockIndexCandidates.insert(pindex); | setBlockIndexCandidates.insert(pindex); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
template <typename F, typename C> | template <typename F, typename C> | ||||
void UpdateFlags(CBlockIndex *pindex, F f, C fchild) { | void CChainState::UpdateFlags(CBlockIndex *pindex, F f, C fchild) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
// Update the current block. | // Update the current block. | ||||
UpdateFlagsForBlock(pindex, pindex, f); | UpdateFlagsForBlock(pindex, pindex, f); | ||||
// 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()) { | ||||
UpdateFlagsForBlock(pindex, it->second, fchild); | UpdateFlagsForBlock(pindex, it->second, fchild); | ||||
it++; | it++; | ||||
} | } | ||||
// Update the flags from all ancestors too. | // Update the flags from all ancestors too. | ||||
while (pindex != nullptr) { | while (pindex != nullptr) { | ||||
BlockStatus newStatus = f(pindex->nStatus); | BlockStatus newStatus = f(pindex->nStatus); | ||||
if (pindex->nStatus != newStatus) { | if (pindex->nStatus != newStatus) { | ||||
pindex->nStatus = newStatus; | pindex->nStatus = newStatus; | ||||
setDirtyBlockIndex.insert(pindex); | setDirtyBlockIndex.insert(pindex); | ||||
} | } | ||||
pindex = pindex->pprev; | pindex = pindex->pprev; | ||||
} | } | ||||
} | } | ||||
template <typename F> void UpdateFlags(CBlockIndex *pindex, F f) { | template <typename F> void CChainState::UpdateFlags(CBlockIndex *pindex, F f) { | ||||
// Handy shorthand. | // Handy shorthand. | ||||
UpdateFlags(pindex, f, f); | UpdateFlags(pindex, f, f); | ||||
} | } | ||||
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; | ||||
} | } | ||||
// In case we are reconsidering something before the finalization point, | // In case we are reconsidering something before the finalization point, | ||||
// move the finalization point to the last common ancestor. | // move the finalization point to the last common ancestor. | ||||
if (pindexFinalized) { | if (pindexFinalized) { | ||||
pindexFinalized = LastCommonAncestor(pindex, pindexFinalized); | pindexFinalized = LastCommonAncestor(pindex, pindexFinalized); | ||||
} | } | ||||
UpdateFlags(pindex, [](const BlockStatus status) { | UpdateFlags(pindex, [](const BlockStatus status) { | ||||
return status.withClearedFailureFlags(); | return status.withClearedFailureFlags(); | ||||
}); | }); | ||||
return true; | return true; | ||||
} | } | ||||
static bool UnparkBlockImpl(CBlockIndex *pindex, bool fClearChildren) { | bool ResetBlockFailureFlags(CBlockIndex *pindex) { | ||||
return g_chainstate.ResetBlockFailureFlags(pindex); | |||||
} | |||||
bool CChainState::UnparkBlockImpl(CBlockIndex *pindex, bool fClearChildren) { | |||||
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; | ||||
} | } | ||||
UpdateFlags(pindex, | UpdateFlags(pindex, | ||||
[](const BlockStatus status) { | [](const BlockStatus status) { | ||||
return status.withClearedParkedFlags(); | return status.withClearedParkedFlags(); | ||||
}, | }, | ||||
[fClearChildren](const BlockStatus status) { | [fClearChildren](const BlockStatus status) { | ||||
return fClearChildren ? status.withClearedParkedFlags() | return fClearChildren ? status.withClearedParkedFlags() | ||||
: status.withParkedParent(false); | : status.withParkedParent(false); | ||||
}); | }); | ||||
return true; | return true; | ||||
} | } | ||||
bool UnparkBlockAndChildren(CBlockIndex *pindex) { | bool UnparkBlockAndChildren(CBlockIndex *pindex) { | ||||
return UnparkBlockImpl(pindex, true); | return g_chainstate.UnparkBlockImpl(pindex, true); | ||||
} | } | ||||
bool UnparkBlock(CBlockIndex *pindex) { | bool UnparkBlock(CBlockIndex *pindex) { | ||||
return UnparkBlockImpl(pindex, false); | return g_chainstate.UnparkBlockImpl(pindex, false); | ||||
} | } | ||||
const CBlockIndex *GetFinalizedBlock() { | const CBlockIndex *GetFinalizedBlock() { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
return pindexFinalized; | return pindexFinalized; | ||||
} | } | ||||
bool IsBlockFinalized(const CBlockIndex *pindex) { | bool IsBlockFinalized(const CBlockIndex *pindex) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
return pindexFinalized && | return pindexFinalized && | ||||
pindexFinalized->GetAncestor(pindex->nHeight) == pindex; | pindexFinalized->GetAncestor(pindex->nHeight) == pindex; | ||||
} | } | ||||
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 28 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 468 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 61 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 38 Lines | |||||
* @param[in] config The global config. | * @param[in] config The global config. | ||||
* @param[in-out] pblock The block we want to accept. | * @param[in-out] pblock The block we want to accept. | ||||
* @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, bool fRequested, | CValidationState &state, bool fRequested, | ||||
const CDiskBlockPos *dbp, bool *fNewBlock) { | const CDiskBlockPos *dbp, 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 *pindex = nullptr; | CBlockIndex *pindex = nullptr; | ||||
▲ Show 20 Lines • Show All 131 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, FlushStateMode::NONE); | FlushStateToDisk(config.GetChainParams(), state, FlushStateMode::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) { | ||||
AssertLockNotHeld(cs_main); | AssertLockNotHeld(cs_main); | ||||
{ | { | ||||
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, fForceProcessing, nullptr, | ret = g_chainstate.AcceptBlock( | ||||
fNewBlock); | config, pblock, state, 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 17 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 222 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(); | ||||
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 20 Lines • Show All 68 Lines • ▼ Show 20 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(), | ||||
FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()), | FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()), | ||||
GuessVerificationProgress(config.GetChainParams().TxData(), | GuessVerificationProgress(config.GetChainParams().TxData(), | ||||
chainActive.Tip())); | chainActive.Tip())); | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 89 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) { | ||||
false); | false); | ||||
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, false); | uiInterface.ShowProgress("", 100, false); | ||||
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, FlushStateMode::ALWAYS)) { | CValidationState state; | ||||
if (!FlushStateToDisk(config.GetChainParams(), state, | |||||
FlushStateMode::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); | ||||
pindexFinalized = nullptr; | pindexFinalized = nullptr; | ||||
pindexBestInvalid = nullptr; | pindexBestInvalid = nullptr; | ||||
pindexBestParked = nullptr; | pindexBestParked = nullptr; | ||||
pindexBestHeader = nullptr; | pindexBestHeader = nullptr; | ||||
g_mempool.clear(); | g_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, true, dbp, | if (g_chainstate.AcceptBlock(config, pblock, state, true, | ||||
nullptr)) { | dbp, nullptr)) { | ||||
nLoaded++; | nLoaded++; | ||||
} | } | ||||
if (state.IsError()) { | if (state.IsError()) { | ||||
break; | break; | ||||
} | } | ||||
} else if (hash != | } else if (hash != | ||||
chainparams.GetConsensus().hashGenesisBlock && | chainparams.GetConsensus().hashGenesisBlock && | ||||
Show All 34 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( | ||||
true, &it->second, nullptr)) { | config, pblockrecursive, dummy, 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 479 Lines • Show Last 20 Lines |