diff --git a/doc/design/assumeutxo.md b/doc/design/assumeutxo.md index ea51b1b87f..652eca7675 100644 --- a/doc/design/assumeutxo.md +++ b/doc/design/assumeutxo.md @@ -107,7 +107,7 @@ 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_stop_use` (not yet +chainstate, we stop use of the background chainstate by setting `m_disabled` (not yet committed - see #15606), in `CompleteSnapshotValidation()`, which is checked in `ActivateBestChain()`). We hash the background chainstate's UTXO set contents and ensure it matches the compiled value in `CMainParams::m_assumeutxo_data`. @@ -119,10 +119,10 @@ The background chainstate data lingers on disk until shutdown, when in | | | | ---------- | ----------- | -| number of chainstates | 2 (ibd has `m_stop_use=true`) | +| number of chainstates | 2 (ibd has `m_disabled=true`) | | active chainstate | snapshot | -**Failure consideration:** if bitcoind unexpectedly halts after `m_stop_use` is set on +**Failure consideration:** if bitcoind unexpectedly halts after `m_disabled` is set on the background chainstate but before `CompleteSnapshotValidation()` can finish, the need to complete snapshot validation will be detected on subsequent init by `ChainstateManager::CheckForUncleanShutdown()`. diff --git a/src/validation.cpp b/src/validation.cpp index 70ea99e6df..9b5875319e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4859,12 +4859,8 @@ std::vector ChainstateManager::GetAll() LOCK(::cs_main); std::vector out; - if (!IsSnapshotValidated() && m_ibd_chainstate) { - out.push_back(m_ibd_chainstate.get()); - } - - if (m_snapshot_chainstate) { - out.push_back(m_snapshot_chainstate.get()); + for (Chainstate* cs : {m_ibd_chainstate.get(), m_snapshot_chainstate.get()}) { + if (this->IsUsable(cs)) out.push_back(cs); } return out; @@ -5263,17 +5259,22 @@ bool ChainstateManager::IsSnapshotActive() const void ChainstateManager::MaybeRebalanceCaches() { AssertLockHeld(::cs_main); - if (m_ibd_chainstate && !m_snapshot_chainstate) { + 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) { LogPrintf("[snapshot] allocating all cache to the IBD chainstate\n"); // Allocate everything to the IBD chainstate. m_ibd_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache); } - else if (m_snapshot_chainstate && !m_ibd_chainstate) { + else if (snapshot_usable && !ibd_usable) { + // If background validation has completed and snapshot is our active chain... LogPrintf("[snapshot] allocating all cache to the snapshot chainstate\n"); // Allocate everything to the snapshot chainstate. m_snapshot_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache); } - else if (m_ibd_chainstate && m_snapshot_chainstate) { + else if (ibd_usable && snapshot_usable) { // 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. diff --git a/src/validation.h b/src/validation.h index 6d4959c44d..2511102b55 100644 --- a/src/validation.h +++ b/src/validation.h @@ -473,6 +473,19 @@ protected: //! Manages the UTXO set, which is a reflection of the contents of `m_chain`. std::unique_ptr 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}; + public: //! Reference to a BlockManager instance which itself is shared across all //! Chainstate instances. @@ -840,10 +853,6 @@ private: //! that call. Chainstate* m_active_chainstate GUARDED_BY(::cs_main) {nullptr}; - //! If true, the assumed-valid chainstate has been fully validated - //! by the background validation chainstate. - bool m_snapshot_validated GUARDED_BY(::cs_main){false}; - CBlockIndex* m_best_invalid GUARDED_BY(::cs_main){nullptr}; //! Internal helper for ActivateSnapshot(). @@ -876,6 +885,15 @@ private: //! nullopt. std::optional GetSnapshotBaseHeight() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + //! 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; + } + public: using Options = kernel::ChainstateManagerOpts; @@ -987,7 +1005,10 @@ public: std::optional SnapshotBlockhash() const; //! Is there a snapshot in use and has it been fully validated? - bool IsSnapshotValidated() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { return m_snapshot_validated; } + bool IsSnapshotValidated() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main) + { + return m_snapshot_chainstate && m_ibd_chainstate && m_ibd_chainstate->m_disabled; + } /** * Process an incoming block. This only returns after the best known valid