merge bitcoin#15976: move methods under CChainState

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
Kittywhiskers Van Gogh 2021-10-09 19:44:44 +05:30
parent 1f436f8aee
commit 1bb1cea5c9
9 changed files with 275 additions and 242 deletions

View File

@ -23,8 +23,8 @@
void CDSNotificationInterface::InitializeCurrentBlockTip()
{
LOCK(cs_main);
SynchronousUpdatedBlockTip(::ChainActive().Tip(), nullptr, IsInitialBlockDownload());
UpdatedBlockTip(::ChainActive().Tip(), nullptr, IsInitialBlockDownload());
SynchronousUpdatedBlockTip(::ChainActive().Tip(), nullptr, ::ChainstateActive().IsInitialBlockDownload());
UpdatedBlockTip(::ChainActive().Tip(), nullptr, ::ChainstateActive().IsInitialBlockDownload());
}
void CDSNotificationInterface::AcceptedBlockHeader(const CBlockIndex *pindexNew)

View File

@ -321,7 +321,7 @@ void PrepareShutdown(InitInterfaces& interfaces)
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
if (pcoinsTip != nullptr) {
FlushStateToDisk();
::ChainstateActive().ForceFlushStateToDisk();
}
// After there are no more peers/RPC left to give us new data which may generate
@ -337,7 +337,7 @@ void PrepareShutdown(InitInterfaces& interfaces)
{
LOCK(cs_main);
if (pcoinsTip != nullptr) {
FlushStateToDisk();
::ChainstateActive().ForceFlushStateToDisk();
}
pcoinsTip.reset();
pcoinscatcher.reset();
@ -988,7 +988,7 @@ void PeriodicStats()
{
assert(gArgs.GetBoolArg("-statsenabled", DEFAULT_STATSD_ENABLE));
CCoinsStats stats;
FlushStateToDisk();
::ChainstateActive().ForceFlushStateToDisk();
if (GetUTXOStats(pcoinsdbview.get(), stats)) {
statsClient.gauge("utxoset.tx", stats.nTransactions, 1.0f);
statsClient.gauge("utxoset.txOutputs", stats.nTransactionOutputs, 1.0f);
@ -2237,7 +2237,7 @@ bool AppInitMain(InitInterfaces& interfaces)
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
uiInterface.InitMessage(_("Pruning blockstore..."));
PruneAndFlush();
::ChainstateActive().PruneAndFlush();
}
}

View File

@ -321,7 +321,7 @@ public:
}
return GuessVerificationProgress(Params().TxData(), tip);
}
bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); }
bool getReindex() override { return ::fReindex; }
bool getImporting() override { return ::fImporting; }
void setNetworkActive(bool active) override

View File

@ -1342,7 +1342,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
// the tip yet so we have no way to check this directly here. Instead we
// just check that there are currently no other blocks in flight.
else if (state.IsValid() &&
!IsInitialBlockDownload() &&
!::ChainstateActive().IsInitialBlockDownload() &&
mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) {
if (it != mapBlockSource.end()) {
MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman);
@ -2023,7 +2023,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
}
// If we're in IBD, we want outbound peers that will serve us a useful
// chain. Disconnect peers that are on chains with insufficient work.
if (IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
if (::ChainstateActive().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
// When nCount < MAX_HEADERS_RESULTS, we know we have no more
// headers to fetch from this peer.
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) {
@ -2555,7 +2555,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!pfrom->fInbound)
{
// Advertise our address
if (fListen && !IsInitialBlockDownload())
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
FastRandomContext insecure_rand;
@ -2862,7 +2862,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
pfrom->GetId());
} else if (!fAlreadyHave) {
bool allowWhileInIBD = allowWhileInIBDObjs.count(inv.type);
if (allowWhileInIBD || (!fImporting && !fReindex && !IsInitialBlockDownload())) {
if (allowWhileInIBD || (!fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload())) {
RequestObject(State(pfrom->GetId()), inv, current_time);
}
}
@ -3023,7 +3023,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
LOCK(cs_main);
if (IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) {
if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
return true;
}
@ -3312,7 +3312,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) {
// Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers
if (!IsInitialBlockDownload())
if (!::ChainstateActive().IsInitialBlockDownload())
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256()));
return true;
}
@ -4310,7 +4310,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
int64_t nNow = GetTimeMicros();
auto current_time = GetTime<std::chrono::microseconds>();
if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
if (!::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
AdvertiseLocal(pto);
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}
@ -4383,7 +4383,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Resend wallet transactions that haven't gotten in a block yet
// Except during reindex, importing and IBD, when old wallet
// transactions become unconfirmed and spams other nodes.
if (!fReindex && !fImporting && !IsInitialBlockDownload())
if (!fReindex && !fImporting && !::ChainstateActive().IsInitialBlockDownload())
{
static int64_t nLastBroadcastTime = 0;
// HACK: Call this only once every few seconds. SendMessages is called once per peer, which makes this signal very expensive
@ -4758,7 +4758,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Message: getdata (blocks)
//
std::vector<CInv> vGetData;
if (!pto->fClient && pto->CanRelay() && ((fFetch && !pto->m_limited_node) || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
if (!pto->fClient && pto->CanRelay() && ((fFetch && !pto->m_limited_node) || !::ChainstateActive().IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
std::vector<const CBlockIndex*> vToDownload;
NodeId staller = -1;
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller, consensusParams);

View File

@ -1263,7 +1263,7 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
UniValue ret(UniValue::VOBJ);
CCoinsStats stats;
FlushStateToDisk();
::ChainstateActive().ForceFlushStateToDisk();
if (GetUTXOStats(pcoinsdbview.get(), stats)) {
ret.pushKV("height", (int64_t)stats.nHeight);
ret.pushKV("bestblock", stats.hashBlock.GetHex());
@ -1537,7 +1537,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
obj.pushKV("difficulty", (double)GetDifficulty(tip));
obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast());
obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip));
obj.pushKV("initialblockdownload", IsInitialBlockDownload());
obj.pushKV("initialblockdownload", ::ChainstateActive().IsInitialBlockDownload());
obj.pushKV("chainwork", tip->nChainWork.GetHex());
obj.pushKV("size_on_disk", CalculateCurrentUsage());
obj.pushKV("pruned", fPruneMode);
@ -2610,7 +2610,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
std::unique_ptr<CCoinsViewCursor> pcursor;
{
LOCK(cs_main);
FlushStateToDisk();
::ChainstateActive().ForceFlushStateToDisk();
pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
assert(pcursor);
}

View File

@ -490,7 +490,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
if (IsInitialBlockDownload())
if (::ChainstateActive().IsInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks...");
// next bock is a superblock and we need governance info to correctly construct it

View File

@ -82,13 +82,8 @@ static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
/** Maximum age of our tip in seconds for us to be considered current for fee estimation */
static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
/**
* Global state
*/
namespace {
struct CBlockIndexWorkComparator
{
bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
{
// First sort by most total work, ...
if (pa->nChainWork > pb->nChainWork) return false;
if (pa->nChainWork < pb->nChainWork) return true;
@ -104,144 +99,11 @@ namespace {
// Identical blocks.
return false;
}
};
} // anon namespace
}
enum DisconnectResult
{
DISCONNECT_OK, // All good.
DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
DISCONNECT_FAILED // Something else went wrong.
};
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;
/**
* Every received block is assigned a unique and increasing identifier, so we
* know which one to give priority in case of a fork.
*/
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). */
int32_t nBlockReverseSequenceId = -1;
/** chainwork for the last block that preciousblock has been applied to. */
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*> m_failed_blocks;
/**
* the ChainState CriticalSection
* A lock that must be held when modifying this ChainState - held in ActivateBestChain()
*/
CCriticalSection m_cs_chainstate;
public:
//! The current chain of blockheaders we consult and build on.
//! @see CChain, CBlockIndex.
CChain m_chain;
BlockMap mapBlockIndex;
PrevBlockMap mapPrevBlockIndex;
std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
CBlockIndex *pindexBestInvalid = nullptr;
bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock);
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
* that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
*/
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block disconnection on our pcoinsTip:
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Manual block validity manipulation:
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
bool LoadGenesisBlock(const CChainParams& chainparams);
bool AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void PruneBlockIndexCandidates();
void UnloadBlockIndex();
private:
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* AddToBlockIndex(const CBlockHeader& block, enum BlockStatus nStatus = BLOCK_VALID_TREE) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Make various assertions about the state of the block index.
*
* By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
*/
void CheckBlockIndex(const Consensus::Params& consensusParams);
void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
} g_chainstate;
CChainState g_chainstate;
CChainState& ChainstateActive() { return g_chainstate; }
CChain& ChainActive() { return g_chainstate.m_chain; }
/**
@ -256,8 +118,8 @@ CChain& ChainActive() { return g_chainstate.m_chain; }
*/
RecursiveMutex cs_main;
BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex;
PrevBlockMap& mapPrevBlockIndex = g_chainstate.mapPrevBlockIndex;
BlockMap& mapBlockIndex = ::ChainstateActive().mapBlockIndex;
PrevBlockMap& mapPrevBlockIndex = ::ChainstateActive().mapPrevBlockIndex;
CBlockIndex *pindexBestHeader = nullptr;
Mutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
@ -295,12 +157,12 @@ static void CheckBlockIndex(const Consensus::Params& consensusParams);
// Internal stuff
namespace {
CBlockIndex *&pindexBestInvalid = g_chainstate.pindexBestInvalid;
CBlockIndex *&pindexBestInvalid = ::ChainstateActive().pindexBestInvalid;
/** 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 = g_chainstate.mapBlocksUnlinked;
std::multimap<CBlockIndex*, CBlockIndex*>& mapBlocksUnlinked = ::ChainstateActive().mapBlocksUnlinked;
CCriticalSection cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile;
@ -338,15 +200,7 @@ std::unique_ptr<CCoinsViewDB> pcoinsdbview;
std::unique_ptr<CCoinsViewCache> pcoinsTip;
std::unique_ptr<CBlockTreeDB> pblocktree;
enum class FlushStateMode {
NONE,
IF_NEEDED,
PERIODIC,
ALWAYS
};
// See definition for documentation
static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
@ -552,7 +406,7 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
if (IsInitialBlockDownload())
if (::ChainstateActive().IsInitialBlockDownload())
return false;
if (::ChainActive().Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
@ -951,7 +805,7 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
::ChainstateActive().FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC);
return res;
}
@ -1252,27 +1106,30 @@ CAmount GetMasternodePayment(int nHeight, CAmount blockValue, int nReallocActiva
return static_cast<CAmount>(blockValue * vecPeriods[nCurrentPeriod] / 1000);
}
bool IsInitialBlockDownload()
// Note that though this is marked const, we may end up modifying `m_cached_finished_ibd`, which
// is a performance-related implementation detail. This function must be marked
// `const` so that `CValidationInterface` clients (which are given a `const CChainState*`)
// can call it.
//
bool CChainState::IsInitialBlockDownload() const
{
// Once this function has returned false, it must remain false.
static std::atomic<bool> latchToFalse{false};
// Optimization: pre-test latch before taking the lock.
if (latchToFalse.load(std::memory_order_relaxed))
if (m_cached_finished_ibd.load(std::memory_order_relaxed))
return false;
LOCK(cs_main);
if (latchToFalse.load(std::memory_order_relaxed))
if (m_cached_finished_ibd.load(std::memory_order_relaxed))
return false;
if (fImporting || fReindex)
return true;
if (::ChainActive().Tip() == nullptr)
if (m_chain.Tip() == nullptr)
return true;
if (::ChainActive().Tip()->nChainWork < nMinimumChainWork)
if (m_chain.Tip()->nChainWork < nMinimumChainWork)
return true;
if (::ChainActive().Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
return true;
LogPrintf("Leaving InitialBlockDownload (latching to false)\n");
latchToFalse.store(true, std::memory_order_relaxed);
m_cached_finished_ibd.store(true, std::memory_order_relaxed);
return false;
}
@ -1301,7 +1158,7 @@ static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
// (we assume we don't get stuck on a fork before finishing our initial sync)
if (IsInitialBlockDownload())
if (::ChainstateActive().IsInitialBlockDownload())
return;
// If our best fork is no longer within 72 blocks (+/- 3 hours if no one mines it)
@ -2473,16 +2330,12 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
return true;
}
/**
* Update the on-disk chain state.
* The caches and indexes are flushed depending on the mode we're called with
* if they're too large, if it's been a while since the last write,
* or always and in all cases if we're in prune mode and are deleting files.
*
* If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
* besides checking if we need to prune.
*/
bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
bool CChainState::FlushStateToDisk(
const CChainParams& chainparams,
CValidationState &state,
FlushStateMode mode,
int nManualPruneHeight)
{
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
LOCK(cs_main);
static int64_t nLastWrite = 0;
@ -2606,7 +2459,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
}
if (full_flush_completed) {
// Update best block in wallet (so we can detect restored wallets).
GetMainSignals().ChainStateFlushed(::ChainActive().GetLocator());
GetMainSignals().ChainStateFlushed(m_chain.GetLocator());
}
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error while flushing: ") + e.what());
@ -2614,19 +2467,20 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
return true;
}
void FlushStateToDisk() {
void CChainState::ForceFlushStateToDisk() {
CValidationState state;
const CChainParams& chainparams = Params();
if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
void PruneAndFlush() {
void CChainState::PruneAndFlush() {
CValidationState state;
fCheckForPruning = true;
const CChainParams& chainparams = Params();
if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
@ -2661,7 +2515,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
}
std::string warningMessages;
if (!IsInitialBlockDownload())
if (!::ChainstateActive().IsInitialBlockDownload())
{
int nUpgraded = 0;
const CBlockIndex* pindex = pindexNew;
@ -3074,7 +2928,7 @@ static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
if (pindexHeader != pindexHeaderOld) {
fNotify = true;
fInitialBlockDownload = IsInitialBlockDownload();
fInitialBlockDownload = ::ChainstateActive().IsInitialBlockDownload();
pindexHeaderOld = pindexHeader;
}
}
@ -3202,7 +3056,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
}
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
return g_chainstate.ActivateBestChain(state, chainparams, std::move(pblock));
return ::ChainstateActive().ActivateBestChain(state, chainparams, std::move(pblock));
}
bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex)
@ -3234,7 +3088,7 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par
return ActivateBestChain(state, params, std::shared_ptr<const CBlock>());
}
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) {
return g_chainstate.PreciousBlock(state, params, pindex);
return ::ChainstateActive().PreciousBlock(state, params, pindex);
}
bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
@ -3314,7 +3168,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
}
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
return g_chainstate.InvalidateBlock(state, chainparams, pindex);
return ::ChainstateActive().InvalidateBlock(state, chainparams, pindex);
}
bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex)
@ -3385,7 +3239,7 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara
}
bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
return g_chainstate.MarkConflictingBlock(state, chainparams, pindex);
return ::ChainstateActive().MarkConflictingBlock(state, chainparams, pindex);
}
void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
@ -3443,7 +3297,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
}
void ResetBlockFailureFlags(CBlockIndex *pindex) {
return g_chainstate.ResetBlockFailureFlags(pindex);
return ::ChainstateActive().ResetBlockFailureFlags(pindex);
}
CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block, enum BlockStatus nStatus)
@ -3936,7 +3790,7 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
if (!g_chainstate.AcceptBlockHeader(header, state, chainparams, &pindex)) {
if (!::ChainstateActive().AcceptBlockHeader(header, state, chainparams, &pindex)) {
if (first_invalid) *first_invalid = header;
return false;
}
@ -4074,7 +3928,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
ret = ::ChainstateActive().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
@ -4085,7 +3939,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
NotifyHeaderTip();
CValidationState state; // Only used to report errors, not invalidity - ignore it
if (!g_chainstate.ActivateBestChain(state, chainparams, pblock))
if (!::ChainstateActive().ActivateBestChain(state, chainparams, pblock))
return error("%s: ActivateBestChain failed: %s", __func__, FormatStateMessage(state));
LogPrintf("%s : ACCEPTED\n", __func__);
@ -4119,7 +3973,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
if (!g_chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
return false;
assert(state.IsValid());
@ -4214,7 +4068,8 @@ void PruneBlockFilesManual(int nManualPruneHeight)
{
CValidationState state;
const CChainParams& chainparams = Params();
if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
if (!::ChainstateActive().FlushStateToDisk(
chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
@ -4258,7 +4113,7 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
// To avoid excessive prune events negating the benefit of high dbcache
// values, we should not prune too rapidly.
// So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
if (IsInitialBlockDownload()) {
if (::ChainstateActive().IsInitialBlockDownload()) {
// Since this is only relevant during IBD, we use a fixed 10%
nBuffer += nPruneTarget / 10;
}
@ -4392,7 +4247,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (!g_chainstate.LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
if (!::ChainstateActive().LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
return false;
// Load block file info
@ -4479,7 +4334,7 @@ bool LoadChainTip(const CChainParams& chainparams)
}
::ChainActive().SetTip(pindex);
g_chainstate.PruneBlockIndexCandidates();
::ChainstateActive().PruneBlockIndexCandidates();
LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n",
::ChainActive().Tip()->GetBlockHash().ToString(), ::ChainActive().Height(),
@ -4555,7 +4410,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = g_chainstate.DisconnectBlock(block, pindex, coins);
DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
@ -4590,7 +4445,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
}
}
@ -4705,7 +4560,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
}
bool ReplayBlocks(const CChainParams& params, CCoinsView* view) {
return g_chainstate.ReplayBlocks(params, view);
return ::ChainstateActive().ReplayBlocks(params, view);
}
void CChainState::UnloadBlockIndex() {
@ -4740,7 +4595,7 @@ void UnloadBlockIndex()
mapBlockIndex.clear();
fHavePruned = false;
g_chainstate.UnloadBlockIndex();
::ChainstateActive().UnloadBlockIndex();
}
bool LoadBlockIndex(const CChainParams& chainparams)
@ -4822,7 +4677,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
bool LoadGenesisBlock(const CChainParams& chainparams)
{
return g_chainstate.LoadGenesisBlock(chainparams);
return ::ChainstateActive().LoadGenesisBlock(chainparams);
}
void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
@ -4887,7 +4742,7 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
CBlockIndex* pindex = LookupBlockIndex(hash);
if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) {
CValidationState state;
if (g_chainstate.AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
if (::ChainstateActive().AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) {
nLoaded++;
}
if (state.IsError()) {
@ -4924,7 +4779,7 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
head.ToString());
LOCK(cs_main);
CValidationState dummy;
if (g_chainstate.AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
if (::ChainstateActive().AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr))
{
nLoaded++;
queue.push_back(pblockrecursive->GetHash());

View File

@ -33,6 +33,7 @@
#include <utility>
#include <vector>
class CChainState;
class CBlockIndex;
class CBlockTreeDB;
class CBlockUndo;
@ -47,6 +48,7 @@ class CValidationState;
class PrecomputedTransactionData;
struct ChainTxData;
struct DisconnectedBlockTransactions;
struct LockPoints;
/** Default for -minrelaytxfee, minimum relay fee for transactions */
@ -234,8 +236,6 @@ void UnloadBlockIndex();
void StartScriptCheckWorkerThreads(int threads_num);
/** Stop all of the script checking worker threads */
void StopScriptCheckWorkerThreads();
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload();
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256& hash, CTransactionRef& tx, const Consensus::Params& params, uint256& hashBlock, const CBlockIndex* const blockIndex = nullptr);
/**
@ -266,10 +266,6 @@ void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
*/
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
/** Flush all state, indexes and buffers to disk. */
void FlushStateToDisk();
/** Prune block files and flush state to disk. */
void PruneAndFlush();
/** Prune block files up to a given height */
void PruneBlockFilesManual(int nManualPruneHeight);
@ -407,6 +403,185 @@ inline CBlockIndex* LookupBlockIndex(const uint256& hash)
/** Find the last common block between the parameter chain and a locator. */
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
enum DisconnectResult {
DISCONNECT_OK, // All good.
DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block.
DISCONNECT_FAILED // Something else went wrong.
};
class ConnectTrace;
/** @see CChainState::FlushStateToDisk */
enum class FlushStateMode {
NONE,
IF_NEEDED,
PERIODIC,
ALWAYS
};
struct CBlockIndexWorkComparator
{
bool operator()(const CBlockIndex *pa, const CBlockIndex *pb) const;
};
/**
* 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;
/**
* Every received block is assigned a unique and increasing identifier, so we
* know which one to give priority in case of a fork.
*/
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). */
int32_t nBlockReverseSequenceId = -1;
/** chainwork for the last block that preciousblock has been applied to. */
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*> m_failed_blocks;
/**
* the ChainState CriticalSection
* A lock that must be held when modifying this ChainState - held in ActivateBestChain()
*/
CCriticalSection m_cs_chainstate;
/**
* Whether this chainstate is undergoing initial block download.
*
* Mutable because we need to be able to mark IsInitialBlockDownload()
* const, which latches this for caching purposes.
*/
mutable std::atomic<bool> m_cached_finished_ibd{false};
public:
//! The current chain of blockheaders we consult and build on.
//! @see CChain, CBlockIndex.
CChain m_chain;
BlockMap mapBlockIndex;
PrevBlockMap mapPrevBlockIndex;
std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
CBlockIndex* pindexBestInvalid = nullptr;
bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Update the on-disk chain state.
* The caches and indexes are flushed depending on the mode we're called with
* if they're too large, if it's been a while since the last write,
* or always and in all cases if we're in prune mode and are deleting files.
*
* If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
* besides checking if we need to prune.
*/
bool FlushStateToDisk(
const CChainParams& chainparams,
CValidationState &state,
FlushStateMode mode,
int nManualPruneHeight = 0);
//! Unconditionally flush all changes to disk.
void ForceFlushStateToDisk();
//! Prune blockfiles from the disk if necessary and then flush chainstate changes
//! if we pruned.
void PruneAndFlush();
bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock);
/**
* If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
* that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
*/
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block disconnection on our pcoinsTip:
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Manual block validity manipulation:
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
bool LoadGenesisBlock(const CChainParams& chainparams);
bool AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void PruneBlockIndexCandidates();
void UnloadBlockIndex();
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
bool IsInitialBlockDownload() const;
private:
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* AddToBlockIndex(const CBlockHeader& block, enum BlockStatus nStatus = BLOCK_VALID_TREE) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/**
* Make various assertions about the state of the block index.
*
* By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
*/
void CheckBlockIndex(const Consensus::Params& consensusParams);
void InvalidBlockFound(CBlockIndex* pindex, const CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
};
/** Mark a block as precious and reorganize.
*
* May not be called in a
@ -426,6 +601,9 @@ void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
/** @returns the most-work chain. */
CChain& ChainActive();
/** @returns the most-work valid chainstate. */
CChainState& ChainstateActive();
/** Global variable that points to the coins database (protected by cs_main) */
extern std::unique_ptr<CCoinsViewDB> pcoinsdbview;

View File

@ -2860,7 +2860,7 @@ static UniValue upgradetohd(const JSONRPCRequest& request)
SecureString secureMnemonic;
secureMnemonic.reserve(256);
if (!generate_mnemonic) {
if (IsInitialBlockDownload()) {
if (::ChainstateActive().IsInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set mnemonic while still in Initial Block Download");
}
secureMnemonic = request.params[0].get_str().c_str();