diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -143,7 +142,6 @@ // ShutdownRequested() getting set, and then does the normal Qt shutdown thing. // -static std::unique_ptr pcoinscatcher; static std::unique_ptr globalVerifyHandle; static boost::thread_group threadGroup; @@ -244,8 +242,10 @@ // 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 @@ -260,12 +260,10 @@ { 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 : node.chain_clients) { @@ -2391,10 +2389,10 @@ const int64_t load_block_index_start_time = GetTimeMillis(); try { LOCK(cs_main); + // This statement makes ::ChainstateActive() usable. + g_chainstate = std::make_unique(); 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: @@ -2467,21 +2465,23 @@ // 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([]() { - uiInterface.ThreadSafeMessageBox( - _("Error reading from database, shutting down.") - .translated, - "", CClientUIInterface::MSG_ERROR); - }); + ::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); + }); // 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; @@ -2489,7 +2489,7 @@ // ReplayBlocks is a no-op if we cleared the coinsviewdb with // -reindex or -reindex-chainstate - if (!ReplayBlocks(params, pcoinsdbview.get())) { + if (!ReplayBlocks(params, &::ChainstateActive().CoinsDB())) { strLoadError = _("Unable to replay blocks. You will need to rebuild " "the database using -reindex-chainstate.") @@ -2498,12 +2498,14 @@ } // The on-disk coinsdb is now in a good state, create the cache - pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get())); + ::ChainstateActive().InitCoinsCache(); + assert(::ChainstateActive().CanFlushToDisk()); - bool is_coinsview_empty = fReset || fReindexChainState || - pcoinsTip->GetBestBlock().IsNull(); + bool is_coinsview_empty = + fReset || fReindexChainState || + ::ChainstateActive().CoinsTip().GetBestBlock().IsNull(); if (!is_coinsview_empty) { - // LoadChainTip sets ::ChainActive() based on pcoinsTip's + // LoadChainTip sets ::ChainActive() based on CoinsTip()'s // best block if (!LoadChainTip(config)) { strLoadError = @@ -2539,7 +2541,7 @@ } if (!CVerifyDB().VerifyDB( - config, pcoinsdbview.get(), + config, &::ChainstateActive().CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1138,7 +1138,7 @@ 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)); @@ -2541,7 +2541,8 @@ { LOCK(cs_main); ::ChainstateActive().ForceFlushStateToDisk(); - pcursor = std::unique_ptr(pcoinsdbview->Cursor()); + pcursor = std::unique_ptr( + ::ChainstateActive().CoinsDB().Cursor()); CHECK_NONFATAL(pcursor); tip = ::ChainActive().Tip(); CHECK_NONFATAL(tip); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -112,8 +112,13 @@ GetMainSignals().RegisterBackgroundSignalScheduler(*g_rpc_node->scheduler); pblocktree.reset(new CBlockTreeDB(1 << 20, true)); - pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true)); - pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get())); + g_chainstate = std::make_unique(); + ::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."); } @@ -152,8 +157,7 @@ m_node.mempool = nullptr; m_node.scheduler.reset(); UnloadBlockIndex(); - pcoinsTip.reset(); - pcoinsdbview.reset(); + g_chainstate.reset(); pblocktree.reset(); } diff --git a/src/txdb.h b/src/txdb.h --- a/src/txdb.h +++ b/src/txdb.h @@ -53,8 +53,12 @@ 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/txdb.cpp b/src/txdb.cpp --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -54,8 +54,9 @@ }; } // namespace -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) {} bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const { return db.Read(CoinEntry(&outpoint), coin); diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -23,6 +23,7 @@ #include