mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-27 02:36:47 +01:00
init: add utxo snapshot detection
Add functionality for activating a snapshot-based chainstate if one is detected on-disk. Also cautiously initialize chainstate cache usages so that we don't somehow blow past our cache allowances during initialization, then rebalance at the end of init. Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
This commit is contained in:
parent
f9f1735f13
commit
252abd1e8b
7 changed files with 87 additions and 34 deletions
|
@ -48,10 +48,15 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
|
|||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
chainman.InitializeChainstate(options.mempool);
|
||||
chainman.m_total_coinstip_cache = cache_sizes.coins;
|
||||
chainman.m_total_coinsdb_cache = cache_sizes.coins_db;
|
||||
|
||||
// Load the fully validated chainstate.
|
||||
chainman.InitializeChainstate(options.mempool);
|
||||
|
||||
// Load a chain created from a UTXO snapshot, if any exist.
|
||||
chainman.DetectSnapshotChainstate(options.mempool);
|
||||
|
||||
auto& pblocktree{chainman.m_blockman.m_block_tree_db};
|
||||
// new CBlockTreeDB tries to delete the existing file, which
|
||||
// fails if it's still open from the previous loop. Close it first:
|
||||
|
@ -98,12 +103,20 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
|
|||
return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
|
||||
}
|
||||
|
||||
// Conservative value which is arbitrarily chosen, as it will ultimately be changed
|
||||
// by a call to `chainman.MaybeRebalanceCaches()`. We just need to make sure
|
||||
// that the sum of the two caches (40%) does not exceed the allowable amount
|
||||
// during this temporary initialization state.
|
||||
double init_cache_fraction = 0.2;
|
||||
|
||||
// At this point we're either in reindex or we've loaded a useful
|
||||
// block tree into BlockIndex()!
|
||||
|
||||
for (Chainstate* chainstate : chainman.GetAll()) {
|
||||
LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
|
||||
|
||||
chainstate->InitCoinsDB(
|
||||
/*cache_size_bytes=*/cache_sizes.coins_db,
|
||||
/*cache_size_bytes=*/chainman.m_total_coinsdb_cache * init_cache_fraction,
|
||||
/*in_memory=*/options.coins_db_in_memory,
|
||||
/*should_wipe=*/options.reindex || options.reindex_chainstate);
|
||||
|
||||
|
@ -125,7 +138,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
|
|||
}
|
||||
|
||||
// The on-disk coinsdb is now in a good state, create the cache
|
||||
chainstate->InitCoinsCache(cache_sizes.coins);
|
||||
chainstate->InitCoinsCache(chainman.m_total_coinstip_cache * init_cache_fraction);
|
||||
assert(chainstate->CanFlushToDisk());
|
||||
|
||||
if (!is_coinsview_empty(chainstate)) {
|
||||
|
@ -146,6 +159,11 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
|
|||
};
|
||||
}
|
||||
|
||||
// Now that chainstates are loaded and we're able to flush to
|
||||
// disk, rebalance the coins caches to desired levels based
|
||||
// on the condition of each chainstate.
|
||||
chainman.MaybeRebalanceCaches();
|
||||
|
||||
return {ChainstateLoadStatus::SUCCESS, {}};
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <logging.h>
|
||||
#include <streams.h>
|
||||
#include <uint256.h>
|
||||
#include <util/system.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
@ -76,4 +77,15 @@ std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
|
|||
return base_blockhash;
|
||||
}
|
||||
|
||||
std::optional<fs::path> FindSnapshotChainstateDir()
|
||||
{
|
||||
fs::path possible_dir =
|
||||
gArgs.GetDataDirNet() / fs::u8path(strprintf("chainstate%s", SNAPSHOT_CHAINSTATE_SUFFIX));
|
||||
|
||||
if (fs::exists(possible_dir)) {
|
||||
return possible_dir;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
|
|
@ -58,8 +58,14 @@ bool WriteSnapshotBaseBlockhash(Chainstate& snapshot_chainstate)
|
|||
std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
//! Suffix appended to the chainstate (leveldb) dir when created based upon
|
||||
//! a snapshot.
|
||||
constexpr std::string_view SNAPSHOT_CHAINSTATE_SUFFIX = "_snapshot";
|
||||
|
||||
|
||||
//! Return a path to the snapshot-based chainstate dir, if one exists.
|
||||
std::optional<fs::path> FindSnapshotChainstateDir();
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H
|
||||
|
|
|
@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
|
|||
// Create a snapshot-based chainstate.
|
||||
//
|
||||
const uint256 snapshot_blockhash = GetRandHash();
|
||||
Chainstate& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
|
||||
Chainstate& c2 = WITH_LOCK(::cs_main, return manager.ActivateExistingSnapshot(
|
||||
&mempool, snapshot_blockhash));
|
||||
chainstates.push_back(&c2);
|
||||
|
||||
|
@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
|
|||
|
||||
// Create a snapshot-based chainstate.
|
||||
//
|
||||
Chainstate& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool, GetRandHash()));
|
||||
Chainstate& c2 = WITH_LOCK(cs_main, return manager.ActivateExistingSnapshot(&mempool, GetRandHash()));
|
||||
chainstates.push_back(&c2);
|
||||
c2.InitCoinsDB(
|
||||
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
|
||||
|
@ -383,7 +383,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
|
|||
BOOST_CHECK_EQUAL(expected_assumed_valid, num_assumed_valid);
|
||||
|
||||
Chainstate& cs2 = WITH_LOCK(::cs_main,
|
||||
return chainman.InitializeChainstate(&mempool, GetRandHash()));
|
||||
return chainman.ActivateExistingSnapshot(&mempool, GetRandHash()));
|
||||
|
||||
reload_all_block_indexes();
|
||||
|
||||
|
|
|
@ -4744,28 +4744,15 @@ std::vector<Chainstate*> ChainstateManager::GetAll()
|
|||
return out;
|
||||
}
|
||||
|
||||
Chainstate& ChainstateManager::InitializeChainstate(
|
||||
CTxMemPool* mempool, const std::optional<uint256>& snapshot_blockhash)
|
||||
Chainstate& ChainstateManager::InitializeChainstate(CTxMemPool* mempool)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
bool is_snapshot = snapshot_blockhash.has_value();
|
||||
std::unique_ptr<Chainstate>& to_modify =
|
||||
is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
|
||||
assert(!m_ibd_chainstate);
|
||||
assert(!m_active_chainstate);
|
||||
|
||||
if (to_modify) {
|
||||
throw std::logic_error("should not be overwriting a chainstate");
|
||||
}
|
||||
to_modify.reset(new Chainstate(mempool, m_blockman, *this, 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;
|
||||
m_ibd_chainstate = std::make_unique<Chainstate>(mempool, m_blockman, *this);
|
||||
m_active_chainstate = m_ibd_chainstate.get();
|
||||
return *m_active_chainstate;
|
||||
}
|
||||
|
||||
const AssumeutxoData* ExpectedAssumeutxo(
|
||||
|
@ -5134,3 +5121,31 @@ ChainstateManager::~ChainstateManager()
|
|||
i.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool ChainstateManager::DetectSnapshotChainstate(CTxMemPool* mempool)
|
||||
{
|
||||
assert(!m_snapshot_chainstate);
|
||||
std::optional<fs::path> path = node::FindSnapshotChainstateDir();
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
std::optional<uint256> base_blockhash = node::ReadSnapshotBaseBlockhash(*path);
|
||||
if (!base_blockhash) {
|
||||
return false;
|
||||
}
|
||||
LogPrintf("[snapshot] detected active snapshot chainstate (%s) - loading\n",
|
||||
fs::PathToString(*path));
|
||||
|
||||
this->ActivateExistingSnapshot(mempool, *base_blockhash);
|
||||
return true;
|
||||
}
|
||||
|
||||
Chainstate& ChainstateManager::ActivateExistingSnapshot(CTxMemPool* mempool, uint256 base_blockhash)
|
||||
{
|
||||
assert(!m_snapshot_chainstate);
|
||||
m_snapshot_chainstate =
|
||||
std::make_unique<Chainstate>(mempool, m_blockman, *this, base_blockhash);
|
||||
LogPrintf("[snapshot] switching active chainstate to %s\n", m_snapshot_chainstate->ToString());
|
||||
m_active_chainstate = m_snapshot_chainstate.get();
|
||||
return *m_snapshot_chainstate;
|
||||
}
|
||||
|
|
|
@ -929,17 +929,11 @@ public:
|
|||
//! coins databases. This will be split somehow across chainstates.
|
||||
int64_t m_total_coinsdb_cache{0};
|
||||
|
||||
//! Instantiate a new chainstate and assign it based upon whether it is
|
||||
//! from a snapshot.
|
||||
//! Instantiate a new chainstate.
|
||||
//!
|
||||
//! @param[in] mempool The mempool to pass to the chainstate
|
||||
// constructor
|
||||
//! @param[in] snapshot_blockhash If given, signify that this chainstate
|
||||
//! is based on a snapshot.
|
||||
Chainstate& InitializeChainstate(
|
||||
CTxMemPool* mempool,
|
||||
const std::optional<uint256>& snapshot_blockhash = std::nullopt)
|
||||
LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
Chainstate& InitializeChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
//! Get all chainstates currently being used.
|
||||
std::vector<Chainstate*> GetAll();
|
||||
|
@ -1053,6 +1047,15 @@ public:
|
|||
* information. */
|
||||
void ReportHeadersPresync(const arith_uint256& work, int64_t height, int64_t timestamp);
|
||||
|
||||
//! When starting up, search the datadir for a chainstate based on a UTXO
|
||||
//! snapshot that is in the process of being validated.
|
||||
bool DetectSnapshotChainstate(CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
//! Switch the active chainstate to one based on a UTXO snapshot that was loaded
|
||||
//! previously.
|
||||
Chainstate& ActivateExistingSnapshot(CTxMemPool* mempool, uint256 base_blockhash)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
~ChainstateManager();
|
||||
};
|
||||
|
||||
|
|
|
@ -55,7 +55,6 @@ class InitStressTest(BitcoinTestFramework):
|
|||
b'Loading P2P addresses',
|
||||
b'Loading banlist',
|
||||
b'Loading block index',
|
||||
b'Switching active chainstate',
|
||||
b'Checking all blk files are present',
|
||||
b'Loaded best chain:',
|
||||
b'init message: Verifying blocks',
|
||||
|
|
Loading…
Add table
Reference in a new issue