Skip to content

Commit 89cdf4d

Browse files
jamesobryanofsky
andcommitted
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 <[email protected]>
1 parent 8e2ecfe commit 89cdf4d

File tree

2 files changed

+209
-0
lines changed

2 files changed

+209
-0
lines changed

src/validation.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <index/txindex.h>
2121
#include <logging.h>
2222
#include <logging/timer.h>
23+
#include <optional.h>
2324
#include <policy/fees.h>
2425
#include <policy/policy.h>
2526
#include <policy/settings.h>
@@ -4950,6 +4951,14 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
49504951
assert(nNodes == forward.size());
49514952
}
49524953

4954+
std::string CChainState::ToString()
4955+
{
4956+
CBlockIndex* tip = m_chain.Tip();
4957+
return strprintf("Chainstate [%s] @ height %d (%s)",
4958+
m_from_snapshot_blockhash.IsNull() ? "ibd" : "snapshot",
4959+
tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null");
4960+
}
4961+
49534962
std::string CBlockFileInfo::ToString() const
49544963
{
49554964
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 @@ class CMainCleanup
51445153
}
51455154
};
51465155
static CMainCleanup instance_of_cmaincleanup;
5156+
5157+
Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
5158+
if (m_active_chainstate != nullptr) {
5159+
// If a snapshot chainstate exists, it will always be our active.
5160+
return m_active_chainstate->m_from_snapshot_blockhash;
5161+
}
5162+
return {};
5163+
}
5164+
5165+
std::vector<CChainState*> ChainstateManager::GetAll()
5166+
{
5167+
std::vector<CChainState*> out;
5168+
5169+
if (!IsSnapshotValidated() && m_ibd_chainstate) {
5170+
out.push_back(m_ibd_chainstate.get());
5171+
}
5172+
5173+
if (m_snapshot_chainstate) {
5174+
out.push_back(m_snapshot_chainstate.get());
5175+
}
5176+
5177+
return out;
5178+
}
5179+
5180+
CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blockhash)
5181+
{
5182+
bool is_snapshot = !snapshot_blockhash.IsNull();
5183+
std::unique_ptr<CChainState>& to_modify =
5184+
is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
5185+
5186+
if (to_modify) {
5187+
throw std::logic_error("should not be overwriting a chainstate");
5188+
}
5189+
5190+
to_modify.reset(new CChainState(snapshot_blockhash));
5191+
5192+
// Snapshot chainstates and initial IBD chaintates always become active.
5193+
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
5194+
LogPrintf("Switching active chainstate to %s\n", to_modify->ToString());
5195+
m_active_chainstate = to_modify.get();
5196+
} else {
5197+
throw std::logic_error("unexpected chainstate activation");
5198+
}
5199+
5200+
return *to_modify;
5201+
}
5202+
5203+
CChain& ChainstateManager::ActiveChain() const
5204+
{
5205+
assert(m_active_chainstate);
5206+
return m_active_chainstate->m_chain;
5207+
}
5208+
5209+
bool ChainstateManager::IsSnapshotActive() const
5210+
{
5211+
return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
5212+
}
5213+
5214+
CChainState& ChainstateManager::ValidatedChainstate() const
5215+
{
5216+
if (m_snapshot_chainstate && IsSnapshotValidated()) {
5217+
return *m_snapshot_chainstate.get();
5218+
}
5219+
assert(m_ibd_chainstate);
5220+
return *m_ibd_chainstate.get();
5221+
}
5222+
5223+
bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
5224+
{
5225+
return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
5226+
}
5227+
5228+
void ChainstateManager::Unload()
5229+
{
5230+
for (CChainState* chainstate : this->GetAll()) {
5231+
chainstate->m_chain.SetTip(nullptr);
5232+
chainstate->UnloadBlockIndex();
5233+
}
5234+
}
5235+
5236+
void ChainstateManager::Reset()
5237+
{
5238+
m_ibd_chainstate.reset();
5239+
m_snapshot_chainstate.reset();
5240+
m_active_chainstate = nullptr;
5241+
m_snapshot_validated = false;
5242+
}

src/validation.h

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <coins.h>
1515
#include <crypto/common.h> // for ReadLE64
1616
#include <fs.h>
17+
#include <optional.h>
1718
#include <policy/feerate.h>
1819
#include <protocol.h> // For CMessageHeader::MessageStartChars
1920
#include <script/script_error.h>
@@ -539,6 +540,9 @@ enum class CoinsCacheSizeState
539540
OK = 0
540541
};
541542

543+
// Defined below, but needed for `friend` usage in CChainState.
544+
class ChainstateManager;
545+
542546
/**
543547
* CChainState stores and provides an API to update our local knowledge of the
544548
* current best chain.
@@ -748,6 +752,8 @@ class CChainState {
748752
size_t max_coins_cache_size_bytes,
749753
size_t max_mempool_size_bytes) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
750754

755+
std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
756+
751757
private:
752758
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);
753759
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 @@ class CChainState {
760766

761767
//! Mark a block as not having block data
762768
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
769+
770+
friend ChainstateManager;
763771
};
764772

765773
/** Mark a block as precious and reorganize.
@@ -775,6 +783,111 @@ bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparam
775783
/** Remove invalidity status from a block and its descendants. */
776784
void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
777785

786+
/**
787+
* Provides an interface for creating and interacting with one or two
788+
* chainstates: an IBD chainstate generated by downloading blocks, and
789+
* an optional snapshot chainstate loaded from a UTXO snapshot. Managed
790+
* chainstates can be maintained at different heights simultaneously.
791+
*
792+
* This class provides abstractions that allow the retrieval of the current
793+
* most-work chainstate ("Active") as well as chainstates which may be in
794+
* background use to validate UTXO snapshots.
795+
*
796+
* Definitions:
797+
*
798+
* *IBD chainstate*: a chainstate whose current state has been "fully"
799+
* validated by the initial block download process.
800+
*
801+
* *Snapshot chainstate*: a chainstate populated by loading in an
802+
* assumeutxo UTXO snapshot.
803+
*
804+
* *Active chainstate*: the chainstate containing the current most-work
805+
* chain. Consulted by most parts of the system (net_processing,
806+
* wallet) as a reflection of the current chain and UTXO set.
807+
* This may either be an IBD chainstate or a snapshot chainstate.
808+
*
809+
* *Background IBD chainstate*: an IBD chainstate for which the
810+
* IBD process is happening in the background while use of the
811+
* active (snapshot) chainstate allows the rest of the system to function.
812+
*
813+
* *Validated chainstate*: the most-work chainstate which has been validated
814+
* locally via initial block download. This will be the snapshot chainstate
815+
* if a snapshot was loaded and all blocks up to the snapshot starting point
816+
* have been downloaded and validated (via background validation), otherwise
817+
* it will be the IBD chainstate.
818+
*/
819+
class ChainstateManager
820+
{
821+
private:
822+
//! The chainstate used under normal operation (i.e. "regular" IBD) or, if
823+
//! a snapshot is in use, for background validation.
824+
//!
825+
//! Its contents (including on-disk data) will be deleted *upon shutdown*
826+
//! after background validation of the snapshot has completed. We do not
827+
//! free the chainstate contents immediately after it finishes validation
828+
//! to cautiously avoid a case where some other part of the system is still
829+
//! using this pointer (e.g. net_processing).
830+
std::unique_ptr<CChainState> m_ibd_chainstate;
831+
832+
//! A chainstate initialized on the basis of a UTXO snapshot. If this is
833+
//! non-null, it is always our active chainstate.
834+
std::unique_ptr<CChainState> m_snapshot_chainstate;
835+
836+
//! Points to either the ibd or snapshot chainstate; indicates our
837+
//! most-work chain.
838+
CChainState* m_active_chainstate{nullptr};
839+
840+
//! If true, the assumed-valid chainstate has been fully validated
841+
//! by the background validation chainstate.
842+
bool m_snapshot_validated{false};
843+
844+
// For access to m_active_chainstate.
845+
friend CChain& ChainActive();
846+
847+
public:
848+
//! Instantiate a new chainstate and assign it based upon whether it is
849+
//! from a snapshot.
850+
//!
851+
//! @param[in] snapshot_blockhash If given, signify that this chainstate
852+
//! is based on a snapshot.
853+
CChainState& InitializeChainstate(const uint256& snapshot_blockhash = uint256())
854+
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
855+
856+
//! Get all chainstates currently being used.
857+
std::vector<CChainState*> GetAll();
858+
859+
//! The most-work chain.
860+
CChain& ActiveChain() const;
861+
int ActiveHeight() const { return ActiveChain().Height(); }
862+
CBlockIndex* ActiveTip() const { return ActiveChain().Tip(); }
863+
864+
bool IsSnapshotActive() const;
865+
866+
Optional<uint256> SnapshotBlockhash() const;
867+
868+
//! Is there a snapshot in use and has it been fully validated?
869+
bool IsSnapshotValidated() const { return m_snapshot_validated; }
870+
871+
//! @returns true if this chainstate is being used to validate an active
872+
//! snapshot in the background.
873+
bool IsBackgroundIBD(CChainState* chainstate) const;
874+
875+
//! Return the most-work chainstate that has been fully validated.
876+
//!
877+
//! During background validation of a snapshot, this is the IBD chain. After
878+
//! background validation has completed, this is the snapshot chain.
879+
CChainState& ValidatedChainstate() const;
880+
881+
CChain& ValidatedChain() const { return ValidatedChainstate().m_chain; }
882+
CBlockIndex* ValidatedTip() const { return ValidatedChain().Tip(); }
883+
884+
//! Unload block index and chain data before shutdown.
885+
void Unload() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
886+
887+
//! Clear (deconstruct) chainstate data.
888+
void Reset();
889+
};
890+
778891
/** @returns the most-work valid chainstate. */
779892
CChainState& ChainstateActive();
780893

0 commit comments

Comments
 (0)