diff --git a/src/init.cpp b/src/init.cpp index bb3ff8d88f..50ae0c6252 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -149,7 +148,6 @@ NODISCARD static bool CreatePidFile() // shutdown thing. // -static std::unique_ptr pcoinscatcher; static std::unique_ptr globalVerifyHandle; static boost::thread_group threadGroup; @@ -234,8 +232,11 @@ void Shutdown(InitInterfaces& interfaces) } // FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing - if (pcoinsTip != nullptr) { - ::ChainstateActive().ForceFlushStateToDisk(); + // + // g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it + // may not have been initialized yet. + if (g_chainstate && g_chainstate->CanFlushToDisk()) { + g_chainstate->ForceFlushStateToDisk(); } // After there are no more peers/RPC left to give us new data which may generate @@ -250,12 +251,10 @@ void Shutdown(InitInterfaces& interfaces) { LOCK(cs_main); - if (pcoinsTip != nullptr) { - ::ChainstateActive().ForceFlushStateToDisk(); + if (g_chainstate && g_chainstate->CanFlushToDisk()) { + g_chainstate->ForceFlushStateToDisk(); + g_chainstate->ResetCoinsViews(); } - pcoinsTip.reset(); - pcoinscatcher.reset(); - pcoinsdbview.reset(); pblocktree.reset(); } for (const auto& client : interfaces.chain_clients) { @@ -1466,10 +1465,10 @@ bool AppInitMain(InitInterfaces& interfaces) bool is_coinsview_empty; try { LOCK(cs_main); + // This statement makes ::ChainstateActive() usable. + g_chainstate = MakeUnique(); UnloadBlockIndex(); - pcoinsTip.reset(); - pcoinsdbview.reset(); - pcoinscatcher.reset(); + // new CBlockTreeDB tries to delete the existing file, which // fails if it's still open from the previous loop. Close it first: pblocktree.reset(); @@ -1520,9 +1519,12 @@ bool AppInitMain(InitInterfaces& interfaces) // At this point we're either in reindex or we've loaded a useful // block tree into BlockIndex()! - pcoinsdbview.reset(new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState)); - pcoinscatcher.reset(new CCoinsViewErrorCatcher(pcoinsdbview.get())); - pcoinscatcher->AddReadErrCallback([]() { + ::ChainstateActive().InitCoinsDB( + /* cache_size_bytes */ nCoinDBCache, + /* in_memory */ false, + /* should_wipe */ fReset || fReindexChainState); + + ::ChainstateActive().CoinsErrorCatcher().AddReadErrCallback([]() { uiInterface.ThreadSafeMessageBox( _("Error reading from database, shutting down.").translated, "", CClientUIInterface::MSG_ERROR); @@ -1530,23 +1532,25 @@ bool AppInitMain(InitInterfaces& interfaces) // If necessary, upgrade from older database format. // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate - if (!pcoinsdbview->Upgrade()) { + if (!::ChainstateActive().CoinsDB().Upgrade()) { strLoadError = _("Error upgrading chainstate database").translated; break; } // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate - if (!ReplayBlocks(chainparams, pcoinsdbview.get())) { + if (!ReplayBlocks(chainparams, &::ChainstateActive().CoinsDB())) { strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated; break; } // The on-disk coinsdb is now in a good state, create the cache - pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get())); + ::ChainstateActive().InitCoinsCache(); + assert(::ChainstateActive().CanFlushToDisk()); - is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull(); + is_coinsview_empty = fReset || fReindexChainState || + ::ChainstateActive().CoinsTip().GetBestBlock().IsNull(); if (!is_coinsview_empty) { - // LoadChainTip sets ::ChainActive() based on pcoinsTip's best block + // LoadChainTip sets ::ChainActive() based on CoinsTip()'s best block if (!LoadChainTip(chainparams)) { strLoadError = _("Error initializing block database").translated; break; @@ -1588,7 +1592,7 @@ bool AppInitMain(InitInterfaces& interfaces) break; } - if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), + if (!CVerifyDB().VerifyDB(chainparams, &::ChainstateActive().CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { strLoadError = _("Corrupted block database detected").translated; break; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c35c478f33..8061939a26 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1062,7 +1062,7 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request) CCoinsStats stats; ::ChainstateActive().ForceFlushStateToDisk(); - if (GetUTXOStats(pcoinsdbview.get(), stats)) { + if (GetUTXOStats(&::ChainstateActive().CoinsDB(), stats)) { ret.pushKV("height", (int64_t)stats.nHeight); ret.pushKV("bestblock", stats.hashBlock.GetHex()); ret.pushKV("transactions", (int64_t)stats.nTransactions); @@ -2206,7 +2206,7 @@ UniValue scantxoutset(const JSONRPCRequest& request) { LOCK(cs_main); ::ChainstateActive().ForceFlushStateToDisk(); - pcursor = std::unique_ptr(pcoinsdbview->Cursor()); + pcursor = std::unique_ptr(::ChainstateActive().CoinsDB().Cursor()); assert(pcursor); } bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins); diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp index de877fd167..0ff081e472 100644 --- a/src/test/setup_common.cpp +++ b/src/test/setup_common.cpp @@ -85,8 +85,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha mempool.setSanityCheck(1.0); pblocktree.reset(new CBlockTreeDB(1 << 20, true)); - pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true)); - pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get())); + g_chainstate = MakeUnique(); + ::ChainstateActive().InitCoinsDB( + /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false); + assert(!::ChainstateActive().CanFlushToDisk()); + ::ChainstateActive().InitCoinsCache(); + assert(::ChainstateActive().CanFlushToDisk()); if (!LoadGenesisBlock(chainparams)) { throw std::runtime_error("LoadGenesisBlock failed."); } @@ -113,8 +117,7 @@ TestingSetup::~TestingSetup() g_connman.reset(); g_banman.reset(); UnloadBlockIndex(); - pcoinsTip.reset(); - pcoinsdbview.reset(); + g_chainstate.reset(); pblocktree.reset(); } diff --git a/src/txdb.cpp b/src/txdb.cpp index df9851396e..18be07e6db 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -52,7 +52,7 @@ struct CoinEntry { } -CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true) +CCoinsViewDB::CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe) : db(ldb_path, nCacheSize, fMemory, fWipe, true) { } diff --git a/src/txdb.h b/src/txdb.h index c4ece11503..140ce2c7ff 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -48,7 +48,10 @@ class CCoinsViewDB final : public CCoinsView protected: CDBWrapper db; public: - explicit CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); + /** + * @param[in] ldb_path Location in the filesystem where leveldb data will be stored. + */ + explicit CCoinsViewDB(fs::path ldb_path, size_t nCacheSize, bool fMemory, bool fWipe); bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; diff --git a/src/validation.cpp b/src/validation.cpp index f9d38a3099..0f24321aaf 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -82,11 +82,17 @@ namespace { BlockManager g_blockman; } // anon namespace -static CChainState g_chainstate(g_blockman); +std::unique_ptr g_chainstate; -CChainState& ChainstateActive() { return g_chainstate; } +CChainState& ChainstateActive() { + assert(g_chainstate); + return *g_chainstate; +} -CChain& ChainActive() { return g_chainstate.m_chain; } +CChain& ChainActive() { + assert(g_chainstate); + return g_chainstate->m_chain; +} /** * Mutex to guard access to validation specific variables, such as reading @@ -173,8 +179,6 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc return chain.Genesis(); } -std::unique_ptr pcoinsdbview; -std::unique_ptr pcoinsTip; std::unique_ptr pblocktree; // See definition for documentation @@ -525,7 +529,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } // Note: this call may add txin.prevout to the coins cache - // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed + // (CoinsTip().cacheCoins) by way of FetchCoin(). It should be removed // later (via coins_to_uncache) if this tx turns out to be invalid. if (!view.HaveCoin(txin.prevout)) { // Are inputs missing because we already have the tx? @@ -1041,6 +1045,40 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) return nSubsidy; } +CoinsViews::CoinsViews( + std::string ldb_name, + size_t cache_size_bytes, + bool in_memory, + bool should_wipe) : m_dbview( + GetDataDir() / ldb_name, cache_size_bytes, in_memory, should_wipe), + m_catcherview(&m_dbview) {} + +void CoinsViews::InitCache() +{ + m_cacheview = MakeUnique(&m_catcherview); +} + +// NOTE: for now m_blockman is set to a global, but this will be changed +// in a future commit. +CChainState::CChainState() : m_blockman(g_blockman) {} + + +void CChainState::InitCoinsDB( + size_t cache_size_bytes, + bool in_memory, + bool should_wipe, + std::string leveldb_name) +{ + m_coins_views = MakeUnique( + leveldb_name, cache_size_bytes, in_memory, should_wipe); +} + +void CChainState::InitCoinsCache() +{ + assert(m_coins_views != nullptr); + m_coins_views->InitCache(); +} + // 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*`) @@ -1982,6 +2020,7 @@ bool CChainState::FlushStateToDisk( { int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); LOCK(cs_main); + assert(this->CanFlushToDisk()); static int64_t nLastWrite = 0; static int64_t nLastFlush = 0; std::set setFilesToPrune; diff --git a/src/validation.h b/src/validation.h index 6661369bf8..2a268d8cab 100644 --- a/src/validation.h +++ b/src/validation.h @@ -19,6 +19,7 @@ #include