aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp280
1 files changed, 209 insertions, 71 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 7ee94f8657..dac1ad40d3 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -20,6 +20,7 @@
#include <index/txindex.h>
#include <logging.h>
#include <logging/timer.h>
+#include <optional.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/settings.h>
@@ -58,6 +59,25 @@
#define MICRO 0.000001
#define MILLI 0.001
+/**
+ * An extra transaction can be added to a package, as long as it only has one
+ * ancestor and is no larger than this. Not really any reason to make this
+ * configurable as it doesn't materially change DoS parameters.
+ */
+static const unsigned int EXTRA_DESCENDANT_TX_SIZE_LIMIT = 10000;
+/** Maximum kilobytes for transactions to store for processing during reorg */
+static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
+/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
+static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
+/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
+static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
+/** Time to wait (in seconds) between writing blocks/block index to disk. */
+static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
+/** Time to wait (in seconds) between flushing chainstate to disk. */
+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;
+
bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
// First sort by most total work, ...
if (pa->nChainWork > pb->nChainWork) return false;
@@ -76,20 +96,19 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
return false;
}
-namespace {
-BlockManager g_blockman;
-} // anon namespace
-
-std::unique_ptr<CChainState> g_chainstate;
+ChainstateManager g_chainman;
-CChainState& ChainstateActive() {
- assert(g_chainstate);
- return *g_chainstate;
+CChainState& ChainstateActive()
+{
+ LOCK(::cs_main);
+ assert(g_chainman.m_active_chainstate);
+ return *g_chainman.m_active_chainstate;
}
-CChain& ChainActive() {
- assert(g_chainstate);
- return g_chainstate->m_chain;
+CChain& ChainActive()
+{
+ LOCK(::cs_main);
+ return ::ChainstateActive().m_chain;
}
/**
@@ -151,8 +170,8 @@ namespace {
CBlockIndex* LookupBlockIndex(const uint256& hash)
{
AssertLockHeld(cs_main);
- BlockMap::const_iterator it = g_blockman.m_block_index.find(hash);
- return it == g_blockman.m_block_index.end() ? nullptr : it->second;
+ BlockMap::const_iterator it = g_chainman.BlockIndex().find(hash);
+ return it == g_chainman.BlockIndex().end() ? nullptr : it->second;
}
CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator)
@@ -1016,7 +1035,7 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs
// scripts (ie, other policy checks pass). We perform the inexpensive
// checks first and avoid hashing and signature verification unless those
// checks pass, to mitigate CPU exhaustion denial-of-service attacks.
- PrecomputedTransactionData txdata(*ptx);
+ PrecomputedTransactionData txdata;
if (!PolicyScriptChecks(args, workspace, txdata)) return false;
@@ -1242,10 +1261,9 @@ void CoinsViews::InitCache()
m_cacheview = MakeUnique<CCoinsViewCache>(&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) {}
-
+CChainState::CChainState(BlockManager& blockman, uint256 from_snapshot_blockhash)
+ : m_blockman(blockman),
+ m_from_snapshot_blockhash(from_snapshot_blockhash) {}
void CChainState::InitCoinsDB(
size_t cache_size_bytes,
@@ -1253,6 +1271,10 @@ void CChainState::InitCoinsDB(
bool should_wipe,
std::string leveldb_name)
{
+ if (!m_from_snapshot_blockhash.IsNull()) {
+ leveldb_name += "_" + m_from_snapshot_blockhash.ToString();
+ }
+
m_coins_views = MakeUnique<CoinsViews>(
leveldb_name, cache_size_bytes, in_memory, should_wipe);
}
@@ -1294,7 +1316,8 @@ static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
BlockMap& BlockIndex()
{
- return g_blockman.m_block_index;
+ LOCK(::cs_main);
+ return g_chainman.m_blockman.m_block_index;
}
static void AlertNotify(const std::string& strMessage)
@@ -1512,6 +1535,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
return true;
}
+ if (!txdata.m_ready) {
+ txdata.Init(tx);
+ }
+
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout);
@@ -1668,10 +1695,11 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
return DISCONNECT_FAILED; // adding output for transaction without known metadata
}
}
- // The potential_overwrite parameter to AddCoin is only allowed to be false if we know for
- // sure that the coin did not already exist in the cache. As we have queried for that above
- // using HaveCoin, we don't need to guess. When fClean is false, a coin already existed and
- // it is an overwrite.
+ // If the coin already exists as an unspent coin in the cache, then the
+ // possible_overwrite parameter to AddCoin must be set to true. We have
+ // already checked whether an unspent coin exists above using HaveCoin, so
+ // we don't need to guess. When fClean is false, an unspent coin already
+ // existed and it is an overwrite.
view.AddCoin(out, std::move(undo), !fClean);
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
@@ -2075,15 +2103,19 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
CBlockUndo blockundo;
+ // Precomputed transaction data pointers must not be invalidated
+ // until after `control` has run the script checks (potentially
+ // in multiple threads). Preallocate the vector size so a new allocation
+ // doesn't invalidate pointers into the vector, and keep txsdata in scope
+ // for as long as `control`.
CCheckQueueControl<CScriptCheck> control(fScriptChecks && g_parallel_script_checks ? &scriptcheckqueue : nullptr);
+ std::vector<PrecomputedTransactionData> txsdata(block.vtx.size());
std::vector<int> prevheights;
CAmount nFees = 0;
int nInputs = 0;
int64_t nSigOpsCost = 0;
blockundo.vtxundo.reserve(block.vtx.size() - 1);
- std::vector<PrecomputedTransactionData> txdata;
- txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
const CTransaction &tx = *(block.vtx[i]);
@@ -2130,13 +2162,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops");
}
- txdata.emplace_back(tx);
if (!tx.IsCoinBase())
{
std::vector<CScriptCheck> vChecks;
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
TxValidationState tx_state;
- if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
+ if (fScriptChecks && !CheckInputScripts(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
// Any transaction validation failure in ConnectBlock is a block consensus failure
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
@@ -3355,7 +3386,14 @@ int GetWitnessCommitmentIndex(const CBlock& block)
int commitpos = -1;
if (!block.vtx.empty()) {
for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
- if (block.vtx[0]->vout[o].scriptPubKey.size() >= 38 && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0]->vout[o].scriptPubKey[1] == 0x24 && block.vtx[0]->vout[o].scriptPubKey[2] == 0xaa && block.vtx[0]->vout[o].scriptPubKey[3] == 0x21 && block.vtx[0]->vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0]->vout[o].scriptPubKey[5] == 0xed) {
+ const CTxOut& vout = block.vtx[0]->vout[o];
+ if (vout.scriptPubKey.size() >= MINIMUM_WITNESS_COMMITMENT &&
+ vout.scriptPubKey[0] == OP_RETURN &&
+ vout.scriptPubKey[1] == 0x24 &&
+ vout.scriptPubKey[2] == 0xaa &&
+ vout.scriptPubKey[3] == 0x21 &&
+ vout.scriptPubKey[4] == 0xa9 &&
+ vout.scriptPubKey[5] == 0xed) {
commitpos = o;
}
}
@@ -3386,7 +3424,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin());
CTxOut out;
out.nValue = 0;
- out.scriptPubKey.resize(38);
+ out.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
out.scriptPubKey[0] = OP_RETURN;
out.scriptPubKey[1] = 0x24;
out.scriptPubKey[2] = 0xaa;
@@ -3443,7 +3481,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
if (fCheckpointsEnabled) {
// Don't accept any forks from the main chain prior to last checkpoint.
// GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our
- // g_blockman.m_block_index.
+ // BlockIndex().
CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
if (pcheckpoint && nHeight < pcheckpoint->nHeight) {
LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight);
@@ -3651,7 +3689,8 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValid
LOCK(cs_main);
for (const CBlockHeader& header : headers) {
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
- bool accepted = g_blockman.AcceptBlockHeader(header, state, chainparams, &pindex);
+ bool accepted = g_chainman.m_blockman.AcceptBlockHeader(
+ header, state, chainparams, &pindex);
::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus());
if (!accepted) {
@@ -3853,7 +3892,7 @@ void PruneOneBlockFile(const int fileNumber)
{
LOCK(cs_LastBlockFile);
- for (const auto& entry : g_blockman.m_block_index) {
+ for (const auto& entry : g_chainman.BlockIndex()) {
CBlockIndex* pindex = entry.second;
if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA;
@@ -3867,12 +3906,12 @@ void PruneOneBlockFile(const int fileNumber)
// to be downloaded again in order to consider its chain, at which
// point it would be considered as a candidate for
// m_blocks_unlinked or setBlockIndexCandidates.
- auto range = g_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
+ auto range = g_chainman.m_blockman.m_blocks_unlinked.equal_range(pindex->pprev);
while (range.first != range.second) {
std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
range.first++;
if (_it->second == pindex) {
- g_blockman.m_blocks_unlinked.erase(_it);
+ g_chainman.m_blockman.m_blocks_unlinked.erase(_it);
}
}
}
@@ -4109,9 +4148,11 @@ void BlockManager::Unload() {
bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
- if (!g_blockman.LoadBlockIndex(
- chainparams.GetConsensus(), *pblocktree, ::ChainstateActive().setBlockIndexCandidates))
+ if (!g_chainman.m_blockman.LoadBlockIndex(
+ chainparams.GetConsensus(), *pblocktree,
+ ::ChainstateActive().setBlockIndexCandidates)) {
return false;
+ }
// Load block file info
pblocktree->ReadLastBlockFile(nLastBlockFile);
@@ -4133,7 +4174,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : g_blockman.m_block_index)
+ for (const std::pair<const uint256, CBlockIndex*>& item : g_chainman.BlockIndex())
{
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
@@ -4510,26 +4551,15 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
PruneBlockIndexCandidates();
CheckBlockIndex(params.GetConsensus());
- }
- }
- return true;
-}
-
-bool RewindBlockIndex(const CChainParams& params) {
- if (!::ChainstateActive().RewindBlockIndex(params)) {
- return false;
- }
-
- LOCK(cs_main);
- if (::ChainActive().Tip() != nullptr) {
- // FlushStateToDisk can possibly read ::ChainActive(). Be conservative
- // and skip it here, we're about to -reindex-chainstate anyway, so
- // it'll get called a bunch real soon.
- BlockValidationState state;
- if (!::ChainstateActive().FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
- LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
- return false;
+ // FlushStateToDisk can possibly read ::ChainActive(). Be conservative
+ // and skip it here, we're about to -reindex-chainstate anyway, so
+ // it'll get called a bunch real soon.
+ BlockValidationState state;
+ if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
+ LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
+ return false;
+ }
}
}
@@ -4547,8 +4577,7 @@ void CChainState::UnloadBlockIndex() {
void UnloadBlockIndex()
{
LOCK(cs_main);
- ::ChainActive().SetTip(nullptr);
- g_blockman.Unload();
+ g_chainman.Unload();
pindexBestInvalid = nullptr;
pindexBestHeader = nullptr;
mempool.clear();
@@ -4561,8 +4590,6 @@ void UnloadBlockIndex()
warningcache[b].clear();
}
fHavePruned = false;
-
- ::ChainstateActive().UnloadBlockIndex();
}
bool LoadBlockIndex(const CChainParams& chainparams)
@@ -4572,7 +4599,7 @@ bool LoadBlockIndex(const CChainParams& chainparams)
if (!fReindex) {
bool ret = LoadBlockIndexDB(chainparams);
if (!ret) return false;
- needs_init = g_blockman.m_block_index.empty();
+ needs_init = g_chainman.m_blockman.m_block_index.empty();
}
if (needs_init) {
@@ -4617,7 +4644,7 @@ bool LoadGenesisBlock(const CChainParams& chainparams)
return ::ChainstateActive().LoadGenesisBlock(chainparams);
}
-bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos *dbp)
+void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
@@ -4629,7 +4656,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE+8, SER_DISK, CLIENT_VERSION);
uint64_t nRewind = blkdat.GetPos();
while (!blkdat.eof()) {
- boost::this_thread::interruption_point();
+ if (ShutdownRequested()) return;
blkdat.SetPos(nRewind);
nRewind++; // start one byte further next time, in case of failure
@@ -4693,7 +4720,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
// Activate the genesis block so normal node progress can continue
if (hash == chainparams.GetConsensus().hashGenesisBlock) {
BlockValidationState state;
- if (!ActivateBestChain(state, chainparams)) {
+ if (!ActivateBestChain(state, chainparams, nullptr)) {
break;
}
}
@@ -4734,9 +4761,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
} catch (const std::runtime_error& e) {
AbortNode(std::string("System error: ") + e.what());
}
- if (nLoaded > 0)
- LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart);
- return nLoaded > 0;
+ LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart);
}
void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
@@ -4923,6 +4948,14 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
assert(nNodes == forward.size());
}
+std::string CChainState::ToString()
+{
+ CBlockIndex* tip = m_chain.Tip();
+ return strprintf("Chainstate [%s] @ height %d (%s)",
+ m_from_snapshot_blockhash.IsNull() ? "ibd" : "snapshot",
+ tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null");
+}
+
std::string CBlockFileInfo::ToString() const
{
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
@@ -4970,6 +5003,7 @@ bool LoadMempool(CTxMemPool& pool)
int64_t expired = 0;
int64_t failed = 0;
int64_t already_there = 0;
+ int64_t unbroadcast = 0;
int64_t nNow = GetTime();
try {
@@ -5023,12 +5057,21 @@ bool LoadMempool(CTxMemPool& pool)
for (const auto& i : mapDeltas) {
pool.PrioritiseTransaction(i.first, i.second);
}
+
+ std::set<uint256> unbroadcast_txids;
+ file >> unbroadcast_txids;
+ unbroadcast = unbroadcast_txids.size();
+
+ for (const auto& txid : unbroadcast_txids) {
+ pool.AddUnbroadcastTx(txid);
+ }
+
} catch (const std::exception& e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
return false;
}
- LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there\n", count, failed, expired, already_there);
+ LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there, %i waiting for initial broadcast\n", count, failed, expired, already_there, unbroadcast);
return true;
}
@@ -5038,6 +5081,7 @@ bool DumpMempool(const CTxMemPool& pool)
std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
+ std::set<uint256> unbroadcast_txids;
static Mutex dump_mutex;
LOCK(dump_mutex);
@@ -5048,6 +5092,7 @@ bool DumpMempool(const CTxMemPool& pool)
mapDeltas[i.first] = i.second;
}
vinfo = pool.infoAll();
+ unbroadcast_txids = pool.GetUnbroadcastTxs();
}
int64_t mid = GetTimeMicros();
@@ -5072,6 +5117,10 @@ bool DumpMempool(const CTxMemPool& pool)
}
file << mapDeltas;
+
+ LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
+ file << unbroadcast_txids;
+
if (!FileCommit(file.Get()))
throw std::runtime_error("FileCommit failed");
file.fclose();
@@ -5110,10 +5159,99 @@ public:
CMainCleanup() {}
~CMainCleanup() {
// block headers
- BlockMap::iterator it1 = g_blockman.m_block_index.begin();
- for (; it1 != g_blockman.m_block_index.end(); it1++)
+ BlockMap::iterator it1 = g_chainman.BlockIndex().begin();
+ for (; it1 != g_chainman.BlockIndex().end(); it1++)
delete (*it1).second;
- g_blockman.m_block_index.clear();
+ g_chainman.BlockIndex().clear();
}
};
static CMainCleanup instance_of_cmaincleanup;
+
+Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
+ if (m_active_chainstate != nullptr) {
+ // If a snapshot chainstate exists, it will always be our active.
+ return m_active_chainstate->m_from_snapshot_blockhash;
+ }
+ return {};
+}
+
+std::vector<CChainState*> ChainstateManager::GetAll()
+{
+ std::vector<CChainState*> out;
+
+ if (!IsSnapshotValidated() && m_ibd_chainstate) {
+ out.push_back(m_ibd_chainstate.get());
+ }
+
+ if (m_snapshot_chainstate) {
+ out.push_back(m_snapshot_chainstate.get());
+ }
+
+ return out;
+}
+
+CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blockhash)
+{
+ bool is_snapshot = !snapshot_blockhash.IsNull();
+ std::unique_ptr<CChainState>& to_modify =
+ is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
+
+ if (to_modify) {
+ throw std::logic_error("should not be overwriting a chainstate");
+ }
+
+ to_modify.reset(new CChainState(m_blockman, snapshot_blockhash));
+
+ // Snapshot chainstates and initial IBD chaintates always become active.
+ if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
+ LogPrintf("Switching active chainstate to %s\n", to_modify->ToString());
+ m_active_chainstate = to_modify.get();
+ } else {
+ throw std::logic_error("unexpected chainstate activation");
+ }
+
+ return *to_modify;
+}
+
+CChain& ChainstateManager::ActiveChain() const
+{
+ assert(m_active_chainstate);
+ return m_active_chainstate->m_chain;
+}
+
+bool ChainstateManager::IsSnapshotActive() const
+{
+ return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
+}
+
+CChainState& ChainstateManager::ValidatedChainstate() const
+{
+ if (m_snapshot_chainstate && IsSnapshotValidated()) {
+ return *m_snapshot_chainstate.get();
+ }
+ assert(m_ibd_chainstate);
+ return *m_ibd_chainstate.get();
+}
+
+bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
+{
+ return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
+}
+
+void ChainstateManager::Unload()
+{
+ for (CChainState* chainstate : this->GetAll()) {
+ chainstate->m_chain.SetTip(nullptr);
+ chainstate->UnloadBlockIndex();
+ }
+
+ m_blockman.Unload();
+}
+
+void ChainstateManager::Reset()
+{
+ m_ibd_chainstate.reset();
+ m_snapshot_chainstate.reset();
+ m_active_chainstate = nullptr;
+ m_snapshot_validated = false;
+}