mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-31 10:41:08 +00:00
refactor: Add Chainstate m_assumeutxo and m_target_utxohash members
Get rid of m_disabled/IsUsable members. Instead of marking chains disabled for different reasons, store chainstate assumeutxo status explicitly and use that information to determine how chains should be treated.
This commit is contained in:
parent
6082c84713
commit
9fe927b6d6
@ -97,14 +97,13 @@ sequentially.
|
||||
### Background chainstate hits snapshot base block
|
||||
|
||||
Once the tip of the background chainstate hits the base block of the snapshot
|
||||
chainstate, we stop use of the background chainstate by setting `m_disabled`, in
|
||||
`MaybeCompleteSnapshotValidation()`, which is checked in `ActivateBestChain()`). We hash the
|
||||
chainstate, we hash the
|
||||
background chainstate's UTXO set contents and ensure it matches the compiled value in
|
||||
`CMainParams::m_assumeutxo_data`.
|
||||
|
||||
| | |
|
||||
| ---------- | ----------- |
|
||||
| number of chainstates | 2 (ibd has `m_disabled=true`) |
|
||||
| number of chainstates | 2 |
|
||||
| active chainstate | snapshot |
|
||||
|
||||
The background chainstate data lingers on disk until the program is restarted.
|
||||
|
||||
@ -1882,6 +1882,7 @@ Chainstate::Chainstate(
|
||||
: m_mempool(mempool),
|
||||
m_blockman(blockman),
|
||||
m_chainman(chainman),
|
||||
m_assumeutxo(from_snapshot_blockhash ? Assumeutxo::UNVALIDATED : Assumeutxo::VALIDATED),
|
||||
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
|
||||
|
||||
const CBlockIndex* Chainstate::SnapshotBase() const
|
||||
@ -3393,12 +3394,11 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
|
||||
// we use m_chainstate_mutex to enforce mutual exclusion so that only one caller may execute this function at a time
|
||||
LOCK(m_chainstate_mutex);
|
||||
|
||||
// Belt-and-suspenders check that we aren't attempting to advance the background
|
||||
// chainstate past the snapshot base block.
|
||||
if (WITH_LOCK(::cs_main, return m_disabled)) {
|
||||
LogError("m_disabled is set - this chainstate should not be in operation. "
|
||||
"Please report this as a bug. %s", CLIENT_BUGREPORT);
|
||||
return false;
|
||||
// Belt-and-suspenders check that we aren't attempting to advance the
|
||||
// chainstate past the target block.
|
||||
if (WITH_LOCK(::cs_main, return m_target_utxohash)) {
|
||||
LogError("%s", STR_INTERNAL_BUG("m_target_utxohash is set - this chainstate should not be in operation."));
|
||||
return Assume(false);
|
||||
}
|
||||
|
||||
CBlockIndex *pindexMostWork = nullptr;
|
||||
@ -5609,7 +5609,7 @@ std::vector<Chainstate*> ChainstateManager::GetAll()
|
||||
std::vector<Chainstate*> out;
|
||||
|
||||
for (Chainstate* cs : {m_ibd_chainstate.get(), m_snapshot_chainstate.get()}) {
|
||||
if (this->IsUsable(cs)) out.push_back(cs);
|
||||
if (cs && cs->m_assumeutxo != Assumeutxo::INVALID && !cs->m_target_utxohash) out.push_back(cs);
|
||||
}
|
||||
|
||||
return out;
|
||||
@ -6071,9 +6071,11 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation()
|
||||
// If a snapshot does not need to be validated...
|
||||
if (m_ibd_chainstate.get() == &this->ActiveChainstate() ||
|
||||
// Or if either chainstate is unusable...
|
||||
!this->IsUsable(m_snapshot_chainstate.get()) ||
|
||||
!this->IsUsable(m_ibd_chainstate.get()) ||
|
||||
!m_snapshot_chainstate ||
|
||||
m_snapshot_chainstate->m_assumeutxo != Assumeutxo::UNVALIDATED ||
|
||||
!m_snapshot_chainstate->m_from_snapshot_blockhash ||
|
||||
!m_ibd_chainstate ||
|
||||
m_ibd_chainstate->m_assumeutxo != Assumeutxo::VALIDATED ||
|
||||
!m_ibd_chainstate->m_chain.Tip() ||
|
||||
// Or the validated chainstate is not targeting the snapshot block...
|
||||
!m_ibd_chainstate->m_target_blockhash ||
|
||||
@ -6112,9 +6114,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation()
|
||||
m_ibd_chainstate->SetTargetBlock(nullptr);
|
||||
|
||||
m_active_chainstate = m_ibd_chainstate.get();
|
||||
m_snapshot_chainstate->m_disabled = true;
|
||||
assert(!this->IsUsable(m_snapshot_chainstate.get()));
|
||||
assert(this->IsUsable(m_ibd_chainstate.get()));
|
||||
m_snapshot_chainstate->m_assumeutxo = Assumeutxo::INVALID;
|
||||
|
||||
auto rename_result = m_snapshot_chainstate->InvalidateCoinsDBOnDisk();
|
||||
if (!rename_result) {
|
||||
@ -6131,7 +6131,6 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation()
|
||||
|
||||
assert(snapshot_base_height == curr_height);
|
||||
assert(snapshot_base_height == index_new.nHeight);
|
||||
assert(this->IsUsable(m_snapshot_chainstate.get()));
|
||||
assert(this->GetAll().size() == 2);
|
||||
|
||||
CCoinsViewDB& ibd_coins_db = m_ibd_chainstate->CoinsDB();
|
||||
@ -6187,7 +6186,8 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation()
|
||||
LogInfo("[snapshot] snapshot beginning at %s has been fully validated",
|
||||
snapshot_blockhash.ToString());
|
||||
|
||||
m_ibd_chainstate->m_disabled = true;
|
||||
m_snapshot_chainstate->m_assumeutxo = Assumeutxo::VALIDATED;
|
||||
m_ibd_chainstate->m_target_utxohash = AssumeutxoHash{ibd_stats.hashSerialized};
|
||||
this->MaybeRebalanceCaches();
|
||||
|
||||
return SnapshotCompletionResult::SUCCESS;
|
||||
@ -6209,34 +6209,30 @@ bool ChainstateManager::IsSnapshotActive() const
|
||||
void ChainstateManager::MaybeRebalanceCaches()
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
bool ibd_usable = this->IsUsable(m_ibd_chainstate.get());
|
||||
bool snapshot_usable = this->IsUsable(m_snapshot_chainstate.get());
|
||||
assert(ibd_usable || snapshot_usable);
|
||||
|
||||
if (ibd_usable && !snapshot_usable) {
|
||||
Chainstate& current_cs{*Assert(m_active_chainstate)};
|
||||
Chainstate* historical_cs{HistoricalChainstate()};
|
||||
if (!historical_cs && !current_cs.m_from_snapshot_blockhash) {
|
||||
// Allocate everything to the IBD chainstate. This will always happen
|
||||
// when we are not using a snapshot.
|
||||
m_ibd_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache);
|
||||
}
|
||||
else if (snapshot_usable && !ibd_usable) {
|
||||
current_cs.ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache);
|
||||
} else if (!historical_cs) {
|
||||
// If background validation has completed and snapshot is our active chain...
|
||||
LogInfo("[snapshot] allocating all cache to the snapshot chainstate");
|
||||
// Allocate everything to the snapshot chainstate.
|
||||
m_snapshot_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache);
|
||||
}
|
||||
else if (ibd_usable && snapshot_usable) {
|
||||
current_cs.ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache);
|
||||
} else {
|
||||
// If both chainstates exist, determine who needs more cache based on IBD status.
|
||||
//
|
||||
// Note: shrink caches first so that we don't inadvertently overwhelm available memory.
|
||||
if (IsInitialBlockDownload()) {
|
||||
m_ibd_chainstate->ResizeCoinsCaches(
|
||||
historical_cs->ResizeCoinsCaches(
|
||||
m_total_coinstip_cache * 0.05, m_total_coinsdb_cache * 0.05);
|
||||
m_snapshot_chainstate->ResizeCoinsCaches(
|
||||
current_cs.ResizeCoinsCaches(
|
||||
m_total_coinstip_cache * 0.95, m_total_coinsdb_cache * 0.95);
|
||||
} else {
|
||||
m_snapshot_chainstate->ResizeCoinsCaches(
|
||||
current_cs.ResizeCoinsCaches(
|
||||
m_total_coinstip_cache * 0.05, m_total_coinsdb_cache * 0.05);
|
||||
m_ibd_chainstate->ResizeCoinsCaches(
|
||||
historical_cs->ResizeCoinsCaches(
|
||||
m_total_coinstip_cache * 0.95, m_total_coinsdb_cache * 0.95);
|
||||
}
|
||||
}
|
||||
@ -6394,12 +6390,7 @@ bool ChainstateManager::DeleteSnapshotChainstate()
|
||||
|
||||
ChainstateRole Chainstate::GetRole() const
|
||||
{
|
||||
if (m_chainman.GetAll().size() <= 1) {
|
||||
return ChainstateRole::NORMAL;
|
||||
}
|
||||
return (this != &m_chainman.ActiveChainstate()) ?
|
||||
ChainstateRole::BACKGROUND :
|
||||
ChainstateRole::ASSUMEDVALID;
|
||||
return m_target_blockhash ? ChainstateRole::BACKGROUND : m_assumeutxo == Assumeutxo::UNVALIDATED ? ChainstateRole::ASSUMEDVALID : ChainstateRole::NORMAL;
|
||||
}
|
||||
|
||||
const CBlockIndex* ChainstateManager::GetSnapshotBaseBlock() const
|
||||
@ -6526,10 +6517,11 @@ std::pair<int, int> ChainstateManager::GetPruneRange(const Chainstate& chainstat
|
||||
}
|
||||
int prune_start{0};
|
||||
|
||||
if (this->GetAll().size() > 1 && m_snapshot_chainstate.get() == &chainstate) {
|
||||
// Leave the blocks in the background IBD chain alone if we're pruning
|
||||
// the snapshot chain.
|
||||
prune_start = *Assert(GetSnapshotBaseHeight()) + 1;
|
||||
if (chainstate.m_from_snapshot_blockhash && chainstate.m_assumeutxo != Assumeutxo::VALIDATED) {
|
||||
// Only prune blocks _after_ the snapshot if this is a snapshot chain
|
||||
// that has not been fully validated yet. The earlier blocks need to be
|
||||
// kept to validate the snapshot
|
||||
prune_start = Assert(chainstate.SnapshotBase())->nHeight + 1;
|
||||
}
|
||||
|
||||
int max_prune = std::max<int>(
|
||||
|
||||
@ -514,6 +514,16 @@ constexpr int64_t LargeCoinsCacheThreshold(int64_t total_space) noexcept
|
||||
total_space - MAX_BLOCK_COINSDB_USAGE_BYTES);
|
||||
}
|
||||
|
||||
//! Chainstate assumeutxo validity.
|
||||
enum class Assumeutxo {
|
||||
//! Every block in the chain has been validated.
|
||||
VALIDATED,
|
||||
//! Blocks after an assumeutxo snapshot have been validated but the snapshot itself has not been validated.
|
||||
UNVALIDATED,
|
||||
//! The assumeutxo snapshot failed validation.
|
||||
INVALID,
|
||||
};
|
||||
|
||||
/**
|
||||
* Chainstate stores and provides an API to update our local knowledge of the
|
||||
* current best chain.
|
||||
@ -545,19 +555,6 @@ protected:
|
||||
//! Manages the UTXO set, which is a reflection of the contents of `m_chain`.
|
||||
std::unique_ptr<CoinsViews> m_coins_views;
|
||||
|
||||
//! This toggle exists for use when doing background validation for UTXO
|
||||
//! snapshots.
|
||||
//!
|
||||
//! In the expected case, it is set once the background validation chain reaches the
|
||||
//! same height as the base of the snapshot and its UTXO set is found to hash to
|
||||
//! the expected assumeutxo value. It signals that we should no longer connect
|
||||
//! blocks to the background chainstate. When set on the background validation
|
||||
//! chainstate, it signifies that we have fully validated the snapshot chainstate.
|
||||
//!
|
||||
//! In the unlikely case that the snapshot chainstate is found to be invalid, this
|
||||
//! is set to true on the snapshot chainstate.
|
||||
bool m_disabled GUARDED_BY(::cs_main) {false};
|
||||
|
||||
//! Cached result of LookupBlockIndex(*m_from_snapshot_blockhash)
|
||||
mutable const CBlockIndex* m_cached_snapshot_base GUARDED_BY(::cs_main){nullptr};
|
||||
|
||||
@ -616,6 +613,11 @@ public:
|
||||
//! @see CChain, CBlockIndex.
|
||||
CChain m_chain;
|
||||
|
||||
//! Assumeutxo state indicating whether all blocks in the chain were
|
||||
//! validated, or if the chainstate is based on an assumeutxo snapshot and
|
||||
//! the snapshot has not been validated.
|
||||
Assumeutxo m_assumeutxo GUARDED_BY(::cs_main);
|
||||
|
||||
/**
|
||||
* The blockhash which is the base of the snapshot this chainstate was created from.
|
||||
*
|
||||
@ -629,6 +631,10 @@ public:
|
||||
//! blocks up to the target block, not newer blocks.
|
||||
std::optional<uint256> m_target_blockhash GUARDED_BY(::cs_main);
|
||||
|
||||
//! Hash of the UTXO set at the target block, computed when the chainstate
|
||||
//! reaches the target block, and null before then.
|
||||
std::optional<AssumeutxoHash> m_target_utxohash GUARDED_BY(::cs_main);
|
||||
|
||||
/**
|
||||
* The base of the snapshot this chainstate was created from.
|
||||
*
|
||||
@ -993,15 +999,6 @@ private:
|
||||
/** Most recent headers presync progress update, for rate-limiting. */
|
||||
MockableSteadyClock::time_point m_last_presync_update GUARDED_BY(GetMutex()){};
|
||||
|
||||
//! Return true if a chainstate is considered usable.
|
||||
//!
|
||||
//! This is false when a background validation chainstate has completed its
|
||||
//! validation of an assumed-valid chainstate, or when a snapshot
|
||||
//! chainstate has been found to be invalid.
|
||||
bool IsUsable(const Chainstate* const cs) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
|
||||
return cs && !cs->m_disabled;
|
||||
}
|
||||
|
||||
//! A queue for script verifications that have to be performed by worker threads.
|
||||
CCheckQueue<CScriptCheck> m_script_check_queue;
|
||||
|
||||
@ -1150,7 +1147,7 @@ public:
|
||||
Chainstate* HistoricalChainstate() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
|
||||
{
|
||||
auto* cs{m_ibd_chainstate.get()};
|
||||
return IsUsable(cs) && cs->m_target_blockhash ? cs : nullptr;
|
||||
return cs && cs->m_target_blockhash && !cs->m_target_utxohash ? cs : nullptr;
|
||||
}
|
||||
|
||||
//! The most-work chain.
|
||||
@ -1179,7 +1176,7 @@ public:
|
||||
//! Is there a snapshot in use and has it been fully validated?
|
||||
bool IsSnapshotValidated() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
return m_snapshot_chainstate && m_ibd_chainstate && m_ibd_chainstate->m_disabled;
|
||||
return m_snapshot_chainstate && m_snapshot_chainstate->m_assumeutxo == Assumeutxo::VALIDATED;
|
||||
}
|
||||
|
||||
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user