mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-06 14:19:59 -05:00
validation: introduce unused ChainstateManager
ChainstateManager is responsible for creating and managing multiple chainstates, and will provide a high-level interface for accessing the appropriate chainstate based upon a certain use. Incorporates feedback from Marco Falke. Additional documentation written by Russ Yanofsky. Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
This commit is contained in:
parent
8e2ecfe249
commit
89cdf4d569
2 changed files with 209 additions and 0 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include <index/txindex.h>
|
#include <index/txindex.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <logging/timer.h>
|
#include <logging/timer.h>
|
||||||
|
#include <optional.h>
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <policy/settings.h>
|
#include <policy/settings.h>
|
||||||
|
@ -4950,6 +4951,14 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
|
||||||
assert(nNodes == forward.size());
|
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
|
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));
|
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
|
||||||
|
@ -5144,3 +5153,90 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
static CMainCleanup instance_of_cmaincleanup;
|
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(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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChainstateManager::Reset()
|
||||||
|
{
|
||||||
|
m_ibd_chainstate.reset();
|
||||||
|
m_snapshot_chainstate.reset();
|
||||||
|
m_active_chainstate = nullptr;
|
||||||
|
m_snapshot_validated = false;
|
||||||
|
}
|
||||||
|
|
113
src/validation.h
113
src/validation.h
|
@ -14,6 +14,7 @@
|
||||||
#include <coins.h>
|
#include <coins.h>
|
||||||
#include <crypto/common.h> // for ReadLE64
|
#include <crypto/common.h> // for ReadLE64
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
|
#include <optional.h>
|
||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
#include <protocol.h> // For CMessageHeader::MessageStartChars
|
#include <protocol.h> // For CMessageHeader::MessageStartChars
|
||||||
#include <script/script_error.h>
|
#include <script/script_error.h>
|
||||||
|
@ -539,6 +540,9 @@ enum class CoinsCacheSizeState
|
||||||
OK = 0
|
OK = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Defined below, but needed for `friend` usage in CChainState.
|
||||||
|
class ChainstateManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CChainState stores and provides an API to update our local knowledge of the
|
* CChainState stores and provides an API to update our local knowledge of the
|
||||||
* current best chain.
|
* current best chain.
|
||||||
|
@ -748,6 +752,8 @@ public:
|
||||||
size_t max_coins_cache_size_bytes,
|
size_t max_coins_cache_size_bytes,
|
||||||
size_t max_mempool_size_bytes) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
size_t max_mempool_size_bytes) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
|
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
|
bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
|
||||||
bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
|
bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
|
||||||
|
@ -760,6 +766,8 @@ private:
|
||||||
|
|
||||||
//! Mark a block as not having block data
|
//! Mark a block as not having block data
|
||||||
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
|
friend ChainstateManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Mark a block as precious and reorganize.
|
/** Mark a block as precious and reorganize.
|
||||||
|
@ -775,6 +783,111 @@ bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparam
|
||||||
/** Remove invalidity status from a block and its descendants. */
|
/** Remove invalidity status from a block and its descendants. */
|
||||||
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an interface for creating and interacting with one or two
|
||||||
|
* chainstates: an IBD chainstate generated by downloading blocks, and
|
||||||
|
* an optional snapshot chainstate loaded from a UTXO snapshot. Managed
|
||||||
|
* chainstates can be maintained at different heights simultaneously.
|
||||||
|
*
|
||||||
|
* This class provides abstractions that allow the retrieval of the current
|
||||||
|
* most-work chainstate ("Active") as well as chainstates which may be in
|
||||||
|
* background use to validate UTXO snapshots.
|
||||||
|
*
|
||||||
|
* Definitions:
|
||||||
|
*
|
||||||
|
* *IBD chainstate*: a chainstate whose current state has been "fully"
|
||||||
|
* validated by the initial block download process.
|
||||||
|
*
|
||||||
|
* *Snapshot chainstate*: a chainstate populated by loading in an
|
||||||
|
* assumeutxo UTXO snapshot.
|
||||||
|
*
|
||||||
|
* *Active chainstate*: the chainstate containing the current most-work
|
||||||
|
* chain. Consulted by most parts of the system (net_processing,
|
||||||
|
* wallet) as a reflection of the current chain and UTXO set.
|
||||||
|
* This may either be an IBD chainstate or a snapshot chainstate.
|
||||||
|
*
|
||||||
|
* *Background IBD chainstate*: an IBD chainstate for which the
|
||||||
|
* IBD process is happening in the background while use of the
|
||||||
|
* active (snapshot) chainstate allows the rest of the system to function.
|
||||||
|
*
|
||||||
|
* *Validated chainstate*: the most-work chainstate which has been validated
|
||||||
|
* locally via initial block download. This will be the snapshot chainstate
|
||||||
|
* if a snapshot was loaded and all blocks up to the snapshot starting point
|
||||||
|
* have been downloaded and validated (via background validation), otherwise
|
||||||
|
* it will be the IBD chainstate.
|
||||||
|
*/
|
||||||
|
class ChainstateManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
//! The chainstate used under normal operation (i.e. "regular" IBD) or, if
|
||||||
|
//! a snapshot is in use, for background validation.
|
||||||
|
//!
|
||||||
|
//! Its contents (including on-disk data) will be deleted *upon shutdown*
|
||||||
|
//! after background validation of the snapshot has completed. We do not
|
||||||
|
//! free the chainstate contents immediately after it finishes validation
|
||||||
|
//! to cautiously avoid a case where some other part of the system is still
|
||||||
|
//! using this pointer (e.g. net_processing).
|
||||||
|
std::unique_ptr<CChainState> m_ibd_chainstate;
|
||||||
|
|
||||||
|
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
|
||||||
|
//! non-null, it is always our active chainstate.
|
||||||
|
std::unique_ptr<CChainState> m_snapshot_chainstate;
|
||||||
|
|
||||||
|
//! Points to either the ibd or snapshot chainstate; indicates our
|
||||||
|
//! most-work chain.
|
||||||
|
CChainState* m_active_chainstate{nullptr};
|
||||||
|
|
||||||
|
//! If true, the assumed-valid chainstate has been fully validated
|
||||||
|
//! by the background validation chainstate.
|
||||||
|
bool m_snapshot_validated{false};
|
||||||
|
|
||||||
|
// For access to m_active_chainstate.
|
||||||
|
friend CChain& ChainActive();
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Instantiate a new chainstate and assign it based upon whether it is
|
||||||
|
//! from a snapshot.
|
||||||
|
//!
|
||||||
|
//! @param[in] snapshot_blockhash If given, signify that this chainstate
|
||||||
|
//! is based on a snapshot.
|
||||||
|
CChainState& InitializeChainstate(const uint256& snapshot_blockhash = uint256())
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
|
//! Get all chainstates currently being used.
|
||||||
|
std::vector<CChainState*> GetAll();
|
||||||
|
|
||||||
|
//! The most-work chain.
|
||||||
|
CChain& ActiveChain() const;
|
||||||
|
int ActiveHeight() const { return ActiveChain().Height(); }
|
||||||
|
CBlockIndex* ActiveTip() const { return ActiveChain().Tip(); }
|
||||||
|
|
||||||
|
bool IsSnapshotActive() const;
|
||||||
|
|
||||||
|
Optional<uint256> SnapshotBlockhash() const;
|
||||||
|
|
||||||
|
//! Is there a snapshot in use and has it been fully validated?
|
||||||
|
bool IsSnapshotValidated() const { return m_snapshot_validated; }
|
||||||
|
|
||||||
|
//! @returns true if this chainstate is being used to validate an active
|
||||||
|
//! snapshot in the background.
|
||||||
|
bool IsBackgroundIBD(CChainState* chainstate) const;
|
||||||
|
|
||||||
|
//! Return the most-work chainstate that has been fully validated.
|
||||||
|
//!
|
||||||
|
//! During background validation of a snapshot, this is the IBD chain. After
|
||||||
|
//! background validation has completed, this is the snapshot chain.
|
||||||
|
CChainState& ValidatedChainstate() const;
|
||||||
|
|
||||||
|
CChain& ValidatedChain() const { return ValidatedChainstate().m_chain; }
|
||||||
|
CBlockIndex* ValidatedTip() const { return ValidatedChain().Tip(); }
|
||||||
|
|
||||||
|
//! Unload block index and chain data before shutdown.
|
||||||
|
void Unload() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
|
//! Clear (deconstruct) chainstate data.
|
||||||
|
void Reset();
|
||||||
|
};
|
||||||
|
|
||||||
/** @returns the most-work valid chainstate. */
|
/** @returns the most-work valid chainstate. */
|
||||||
CChainState& ChainstateActive();
|
CChainState& ChainstateActive();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue