mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-05-05 01:10:54 +02:00
simplify ChainstateManager::SnapshotBlockhash() return semantics
Don't return null snapshotblockhash values to avoid caller complexity/confusion.
This commit is contained in:
parent
7a6c46b37e
commit
f6e2da5fb7
13
src/chain.h
13
src/chain.h
@ -163,14 +163,27 @@ public:
|
|||||||
|
|
||||||
//! Number of transactions in this block.
|
//! Number of transactions in this block.
|
||||||
//! Note: in a potential headers-first mode, this number cannot be relied upon
|
//! Note: in a potential headers-first mode, this number cannot be relied upon
|
||||||
|
//! Note: this value is faked during UTXO snapshot load to ensure that
|
||||||
|
//! LoadBlockIndex() will load index entries for blocks that we lack data for.
|
||||||
|
//! @sa ActivateSnapshot
|
||||||
unsigned int nTx{0};
|
unsigned int nTx{0};
|
||||||
|
|
||||||
//! (memory only) Number of transactions in the chain up to and including this block.
|
//! (memory only) Number of transactions in the chain up to and including this block.
|
||||||
//! This value will be non-zero only if and only if transactions for this block and all its parents are available.
|
//! This value will be non-zero only if and only if transactions for this block and all its parents are available.
|
||||||
//! Change to 64-bit type when necessary; won't happen before 2030
|
//! Change to 64-bit type when necessary; won't happen before 2030
|
||||||
|
//!
|
||||||
|
//! Note: this value is faked during use of a UTXO snapshot because we don't
|
||||||
|
//! have the underlying block data available during snapshot load.
|
||||||
|
//! @sa AssumeutxoData
|
||||||
|
//! @sa ActivateSnapshot
|
||||||
unsigned int nChainTx{0};
|
unsigned int nChainTx{0};
|
||||||
|
|
||||||
//! Verification status of this block. See enum BlockStatus
|
//! Verification status of this block. See enum BlockStatus
|
||||||
|
//!
|
||||||
|
//! Note: this value is modified to show BLOCK_OPT_WITNESS during UTXO snapshot
|
||||||
|
//! load to avoid the block index being spuriously rewound.
|
||||||
|
//! @sa RewindBlockIndex
|
||||||
|
//! @sa ActivateSnapshot
|
||||||
uint32_t nStatus{0};
|
uint32_t nStatus{0};
|
||||||
|
|
||||||
//! block header
|
//! block header
|
||||||
|
@ -97,6 +97,14 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
|
|||||||
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
|
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) {
|
||||||
|
cachedCoinsUsage += coin.DynamicMemoryUsage();
|
||||||
|
cacheCoins.emplace(
|
||||||
|
std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(std::move(outpoint)),
|
||||||
|
std::forward_as_tuple(std::move(coin), CCoinsCacheEntry::DIRTY));
|
||||||
|
}
|
||||||
|
|
||||||
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
|
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
|
||||||
bool fCoinbase = tx.IsCoinBase();
|
bool fCoinbase = tx.IsCoinBase();
|
||||||
const uint256& txid = tx.GetHash();
|
const uint256& txid = tx.GetHash();
|
||||||
|
12
src/coins.h
12
src/coins.h
@ -20,6 +20,8 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class ChainstateManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A UTXO entry.
|
* A UTXO entry.
|
||||||
*
|
*
|
||||||
@ -125,6 +127,7 @@ struct CCoinsCacheEntry
|
|||||||
|
|
||||||
CCoinsCacheEntry() : flags(0) {}
|
CCoinsCacheEntry() : flags(0) {}
|
||||||
explicit CCoinsCacheEntry(Coin&& coin_) : coin(std::move(coin_)), flags(0) {}
|
explicit CCoinsCacheEntry(Coin&& coin_) : coin(std::move(coin_)), flags(0) {}
|
||||||
|
CCoinsCacheEntry(Coin&& coin_, unsigned char flag) : coin(std::move(coin_)), flags(flag) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::unordered_map<COutPoint, CCoinsCacheEntry, SaltedOutpointHasher> CCoinsMap;
|
typedef std::unordered_map<COutPoint, CCoinsCacheEntry, SaltedOutpointHasher> CCoinsMap;
|
||||||
@ -262,6 +265,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite);
|
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emplace a coin into cacheCoins without performing any checks, marking
|
||||||
|
* the emplaced coin as dirty.
|
||||||
|
*
|
||||||
|
* NOT FOR GENERAL USE. Used only when loading coins from a UTXO snapshot.
|
||||||
|
* @sa ChainstateManager::PopulateAndValidateSnapshot()
|
||||||
|
*/
|
||||||
|
void EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spend a coin. Pass moveto in order to get the deleted data.
|
* Spend a coin. Pass moveto in order to get the deleted data.
|
||||||
* If no unspent output exists for the passed outpoint, this call
|
* If no unspent output exists for the passed outpoint, this call
|
||||||
|
@ -28,6 +28,8 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
|
|||||||
std::vector<CChainState*> chainstates;
|
std::vector<CChainState*> chainstates;
|
||||||
const CChainParams& chainparams = Params();
|
const CChainParams& chainparams = Params();
|
||||||
|
|
||||||
|
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
|
||||||
|
|
||||||
// Create a legacy (IBD) chainstate.
|
// Create a legacy (IBD) chainstate.
|
||||||
//
|
//
|
||||||
CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool));
|
CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool));
|
||||||
@ -54,10 +56,17 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
|
|||||||
auto& validated_cs = manager.ValidatedChainstate();
|
auto& validated_cs = manager.ValidatedChainstate();
|
||||||
BOOST_CHECK_EQUAL(&validated_cs, &c1);
|
BOOST_CHECK_EQUAL(&validated_cs, &c1);
|
||||||
|
|
||||||
|
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
|
||||||
|
|
||||||
// Create a snapshot-based chainstate.
|
// Create a snapshot-based chainstate.
|
||||||
//
|
//
|
||||||
CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool, GetRandHash()));
|
const uint256 snapshot_blockhash = GetRandHash();
|
||||||
|
CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
|
||||||
|
mempool, snapshot_blockhash));
|
||||||
chainstates.push_back(&c2);
|
chainstates.push_back(&c2);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
|
||||||
|
|
||||||
c2.InitCoinsDB(
|
c2.InitCoinsDB(
|
||||||
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
||||||
WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23));
|
WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23));
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
#include <index/txindex.h>
|
#include <index/txindex.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <logging/timer.h>
|
#include <logging/timer.h>
|
||||||
|
#include <node/coinstats.h>
|
||||||
#include <node/ui_interface.h>
|
#include <node/ui_interface.h>
|
||||||
#include <optional.h>
|
#include <optional.h>
|
||||||
#include <node/coinstats.h>
|
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <policy/settings.h>
|
#include <policy/settings.h>
|
||||||
#include <pow.h>
|
#include <pow.h>
|
||||||
@ -5148,7 +5148,8 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
|
|||||||
|
|
||||||
Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
|
Optional<uint256> ChainstateManager::SnapshotBlockhash() const {
|
||||||
LOCK(::cs_main);
|
LOCK(::cs_main);
|
||||||
if (m_active_chainstate != nullptr) {
|
if (m_active_chainstate != nullptr &&
|
||||||
|
!m_active_chainstate->m_from_snapshot_blockhash.IsNull()) {
|
||||||
// If a snapshot chainstate exists, it will always be our active.
|
// If a snapshot chainstate exists, it will always be our active.
|
||||||
return m_active_chainstate->m_from_snapshot_blockhash;
|
return m_active_chainstate->m_from_snapshot_blockhash;
|
||||||
}
|
}
|
||||||
@ -5205,6 +5206,283 @@ const AssumeutxoData* ExpectedAssumeutxo(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChainstateManager::ActivateSnapshot(
|
||||||
|
CAutoFile& coins_file,
|
||||||
|
const SnapshotMetadata& metadata,
|
||||||
|
bool in_memory)
|
||||||
|
{
|
||||||
|
uint256 base_blockhash = metadata.m_base_blockhash;
|
||||||
|
|
||||||
|
if (this->SnapshotBlockhash()) {
|
||||||
|
LogPrintf("[snapshot] can't activate a snapshot-based chainstate more than once\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t current_coinsdb_cache_size{0};
|
||||||
|
int64_t current_coinstip_cache_size{0};
|
||||||
|
|
||||||
|
// Cache percentages to allocate to each chainstate.
|
||||||
|
//
|
||||||
|
// These particular percentages don't matter so much since they will only be
|
||||||
|
// relevant during snapshot activation; caches are rebalanced at the conclusion of
|
||||||
|
// this function. We want to give (essentially) all available cache capacity to the
|
||||||
|
// snapshot to aid the bulk load later in this function.
|
||||||
|
static constexpr double IBD_CACHE_PERC = 0.01;
|
||||||
|
static constexpr double SNAPSHOT_CACHE_PERC = 0.99;
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(::cs_main);
|
||||||
|
// Resize the coins caches to ensure we're not exceeding memory limits.
|
||||||
|
//
|
||||||
|
// Allocate the majority of the cache to the incoming snapshot chainstate, since
|
||||||
|
// (optimistically) getting to its tip will be the top priority. We'll need to call
|
||||||
|
// `MaybeRebalanceCaches()` once we're done with this function to ensure
|
||||||
|
// the right allocation (including the possibility that no snapshot was activated
|
||||||
|
// and that we should restore the active chainstate caches to their original size).
|
||||||
|
//
|
||||||
|
current_coinsdb_cache_size = this->ActiveChainstate().m_coinsdb_cache_size_bytes;
|
||||||
|
current_coinstip_cache_size = this->ActiveChainstate().m_coinstip_cache_size_bytes;
|
||||||
|
|
||||||
|
// Temporarily resize the active coins cache to make room for the newly-created
|
||||||
|
// snapshot chain.
|
||||||
|
this->ActiveChainstate().ResizeCoinsCaches(
|
||||||
|
static_cast<size_t>(current_coinstip_cache_size * IBD_CACHE_PERC),
|
||||||
|
static_cast<size_t>(current_coinsdb_cache_size * IBD_CACHE_PERC));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto snapshot_chainstate = WITH_LOCK(::cs_main, return MakeUnique<CChainState>(
|
||||||
|
this->ActiveChainstate().m_mempool, m_blockman, base_blockhash));
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(::cs_main);
|
||||||
|
snapshot_chainstate->InitCoinsDB(
|
||||||
|
static_cast<size_t>(current_coinsdb_cache_size * SNAPSHOT_CACHE_PERC),
|
||||||
|
in_memory, false, "chainstate");
|
||||||
|
snapshot_chainstate->InitCoinsCache(
|
||||||
|
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool snapshot_ok = this->PopulateAndValidateSnapshot(
|
||||||
|
*snapshot_chainstate, coins_file, metadata);
|
||||||
|
|
||||||
|
if (!snapshot_ok) {
|
||||||
|
WITH_LOCK(::cs_main, this->MaybeRebalanceCaches());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(::cs_main);
|
||||||
|
assert(!m_snapshot_chainstate);
|
||||||
|
m_snapshot_chainstate.swap(snapshot_chainstate);
|
||||||
|
const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip(::Params());
|
||||||
|
assert(chaintip_loaded);
|
||||||
|
|
||||||
|
m_active_chainstate = m_snapshot_chainstate.get();
|
||||||
|
|
||||||
|
LogPrintf("[snapshot] successfully activated snapshot %s\n", base_blockhash.ToString());
|
||||||
|
LogPrintf("[snapshot] (%.2f MB)\n",
|
||||||
|
m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000));
|
||||||
|
|
||||||
|
this->MaybeRebalanceCaches();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChainstateManager::PopulateAndValidateSnapshot(
|
||||||
|
CChainState& snapshot_chainstate,
|
||||||
|
CAutoFile& coins_file,
|
||||||
|
const SnapshotMetadata& metadata)
|
||||||
|
{
|
||||||
|
// It's okay to release cs_main before we're done using `coins_cache` because we know
|
||||||
|
// that nothing else will be referencing the newly created snapshot_chainstate yet.
|
||||||
|
CCoinsViewCache& coins_cache = *WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsTip());
|
||||||
|
|
||||||
|
uint256 base_blockhash = metadata.m_base_blockhash;
|
||||||
|
|
||||||
|
COutPoint outpoint;
|
||||||
|
Coin coin;
|
||||||
|
const uint64_t coins_count = metadata.m_coins_count;
|
||||||
|
uint64_t coins_left = metadata.m_coins_count;
|
||||||
|
|
||||||
|
LogPrintf("[snapshot] loading coins from snapshot %s\n", base_blockhash.ToString());
|
||||||
|
int64_t flush_now{0};
|
||||||
|
int64_t coins_processed{0};
|
||||||
|
|
||||||
|
while (coins_left > 0) {
|
||||||
|
try {
|
||||||
|
coins_file >> outpoint;
|
||||||
|
} catch (const std::ios_base::failure&) {
|
||||||
|
LogPrintf("[snapshot] bad snapshot - no coins left after deserializing %d coins\n",
|
||||||
|
coins_count - coins_left);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
coins_file >> coin;
|
||||||
|
coins_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin));
|
||||||
|
|
||||||
|
--coins_left;
|
||||||
|
++coins_processed;
|
||||||
|
|
||||||
|
if (coins_processed % 1000000 == 0) {
|
||||||
|
LogPrintf("[snapshot] %d coins loaded (%.2f%%, %.2f MB)\n",
|
||||||
|
coins_processed,
|
||||||
|
static_cast<float>(coins_processed) * 100 / static_cast<float>(coins_count),
|
||||||
|
coins_cache.DynamicMemoryUsage() / (1000 * 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch write and flush (if we need to) every so often.
|
||||||
|
//
|
||||||
|
// If our average Coin size is roughly 41 bytes, checking every 120,000 coins
|
||||||
|
// means <5MB of memory imprecision.
|
||||||
|
if (coins_processed % 120000 == 0) {
|
||||||
|
if (ShutdownRequested()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto snapshot_cache_state = WITH_LOCK(::cs_main,
|
||||||
|
return snapshot_chainstate.GetCoinsCacheSizeState(&snapshot_chainstate.m_mempool));
|
||||||
|
|
||||||
|
if (snapshot_cache_state >=
|
||||||
|
CoinsCacheSizeState::CRITICAL) {
|
||||||
|
LogPrintf("[snapshot] flushing coins cache (%.2f MB)... ", /* Continued */
|
||||||
|
coins_cache.DynamicMemoryUsage() / (1000 * 1000));
|
||||||
|
flush_now = GetTimeMillis();
|
||||||
|
|
||||||
|
// This is a hack - we don't know what the actual best block is, but that
|
||||||
|
// doesn't matter for the purposes of flushing the cache here. We'll set this
|
||||||
|
// to its correct value (`base_blockhash`) below after the coins are loaded.
|
||||||
|
coins_cache.SetBestBlock(GetRandHash());
|
||||||
|
|
||||||
|
coins_cache.Flush();
|
||||||
|
LogPrintf("done (%.2fms)\n", GetTimeMillis() - flush_now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Important that we set this. This and the coins_cache accesses above are
|
||||||
|
// sort of a layer violation, but either we reach into the innards of
|
||||||
|
// CCoinsViewCache here or we have to invert some of the CChainState to
|
||||||
|
// embed them in a snapshot-activation-specific CCoinsViewCache bulk load
|
||||||
|
// method.
|
||||||
|
coins_cache.SetBestBlock(base_blockhash);
|
||||||
|
|
||||||
|
bool out_of_coins{false};
|
||||||
|
try {
|
||||||
|
coins_file >> outpoint;
|
||||||
|
} catch (const std::ios_base::failure&) {
|
||||||
|
// We expect an exception since we should be out of coins.
|
||||||
|
out_of_coins = true;
|
||||||
|
}
|
||||||
|
if (!out_of_coins) {
|
||||||
|
LogPrintf("[snapshot] bad snapshot - coins left over after deserializing %d coins\n",
|
||||||
|
coins_count);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogPrintf("[snapshot] loaded %d (%.2f MB) coins from snapshot %s\n",
|
||||||
|
coins_count,
|
||||||
|
coins_cache.DynamicMemoryUsage() / (1000 * 1000),
|
||||||
|
base_blockhash.ToString());
|
||||||
|
|
||||||
|
LogPrintf("[snapshot] flushing snapshot chainstate to disk\n");
|
||||||
|
// No need to acquire cs_main since this chainstate isn't being used yet.
|
||||||
|
coins_cache.Flush(); // TODO: if #17487 is merged, add erase=false here for better performance.
|
||||||
|
|
||||||
|
assert(coins_cache.GetBestBlock() == base_blockhash);
|
||||||
|
|
||||||
|
CCoinsStats stats;
|
||||||
|
auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
|
||||||
|
|
||||||
|
// As above, okay to immediately release cs_main here since no other context knows
|
||||||
|
// about the snapshot_chainstate.
|
||||||
|
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
|
||||||
|
|
||||||
|
if (!GetUTXOStats(snapshot_coinsdb, stats, CoinStatsHashType::HASH_SERIALIZED, breakpoint_fnc)) {
|
||||||
|
LogPrintf("[snapshot] failed to generate coins stats\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the base blockhash appears in the known chain of valid headers. We're willing to
|
||||||
|
// wait a bit here because the snapshot may have been loaded on startup, before we've
|
||||||
|
// received headers from the network.
|
||||||
|
|
||||||
|
int max_secs_to_wait_for_headers = 60 * 10;
|
||||||
|
CBlockIndex* snapshot_start_block = nullptr;
|
||||||
|
|
||||||
|
while (max_secs_to_wait_for_headers > 0) {
|
||||||
|
snapshot_start_block = WITH_LOCK(::cs_main,
|
||||||
|
return m_blockman.LookupBlockIndex(base_blockhash));
|
||||||
|
--max_secs_to_wait_for_headers;
|
||||||
|
|
||||||
|
if (!snapshot_start_block) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot_start_block == nullptr) {
|
||||||
|
LogPrintf("[snapshot] timed out waiting for snapshot start blockheader %s\n",
|
||||||
|
base_blockhash.ToString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that the deserialized chainstate contents match the expected assumeutxo value.
|
||||||
|
|
||||||
|
int base_height = snapshot_start_block->nHeight;
|
||||||
|
auto maybe_au_data = ExpectedAssumeutxo(base_height, ::Params());
|
||||||
|
|
||||||
|
if (!maybe_au_data) {
|
||||||
|
LogPrintf("[snapshot] assumeutxo height in snapshot metadata not recognized " /* Continued */
|
||||||
|
"(%d) - refusing to load snapshot\n", base_height);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AssumeutxoData& au_data = *maybe_au_data;
|
||||||
|
|
||||||
|
if (stats.hashSerialized != au_data.hash_serialized) {
|
||||||
|
LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
|
||||||
|
au_data.hash_serialized.ToString(), stats.hashSerialized.ToString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot_chainstate.m_chain.SetTip(snapshot_start_block);
|
||||||
|
|
||||||
|
// The remainder of this function requires modifying data protected by cs_main.
|
||||||
|
LOCK(::cs_main);
|
||||||
|
|
||||||
|
// Fake various pieces of CBlockIndex state:
|
||||||
|
//
|
||||||
|
// - nChainTx: so that we accurately report IBD-to-tip progress
|
||||||
|
// - nTx: so that LoadBlockIndex() loads assumed-valid CBlockIndex entries
|
||||||
|
// (among other things)
|
||||||
|
// - nStatus & BLOCK_OPT_WITNESS: so that RewindBlockIndex() doesn't zealously
|
||||||
|
// unwind the assumed-valid chain.
|
||||||
|
//
|
||||||
|
CBlockIndex* index = nullptr;
|
||||||
|
for (int i = 0; i <= snapshot_chainstate.m_chain.Height(); ++i) {
|
||||||
|
index = snapshot_chainstate.m_chain[i];
|
||||||
|
|
||||||
|
if (!index->nTx) {
|
||||||
|
index->nTx = 1;
|
||||||
|
}
|
||||||
|
index->nChainTx = index->pprev ? index->pprev->nChainTx + index->nTx : 1;
|
||||||
|
|
||||||
|
// We need to fake this flag so that CChainState::RewindBlockIndex()
|
||||||
|
// won't try to rewind the entire assumed-valid chain on startup.
|
||||||
|
if (index->pprev && ::IsWitnessEnabled(index->pprev, ::Params().GetConsensus())) {
|
||||||
|
index->nStatus |= BLOCK_OPT_WITNESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(index);
|
||||||
|
index->nChainTx = metadata.m_nchaintx;
|
||||||
|
snapshot_chainstate.setBlockIndexCandidates.insert(snapshot_start_block);
|
||||||
|
|
||||||
|
LogPrintf("[snapshot] validated snapshot (%.2f MB)\n",
|
||||||
|
coins_cache.DynamicMemoryUsage() / (1000 * 1000));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
CChainState& ChainstateManager::ActiveChainstate() const
|
CChainState& ChainstateManager::ActiveChainstate() const
|
||||||
{
|
{
|
||||||
LOCK(::cs_main);
|
LOCK(::cs_main);
|
||||||
|
@ -11,10 +11,12 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <amount.h>
|
#include <amount.h>
|
||||||
|
#include <attributes.h>
|
||||||
#include <coins.h>
|
#include <coins.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <crypto/common.h> // for ReadLE64
|
#include <crypto/common.h> // for ReadLE64
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
|
#include <node/utxo_snapshot.h>
|
||||||
#include <optional.h>
|
#include <optional.h>
|
||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
#include <protocol.h> // For CMessageHeader::MessageStartChars
|
#include <protocol.h> // For CMessageHeader::MessageStartChars
|
||||||
@ -870,6 +872,12 @@ private:
|
|||||||
//! by the background validation chainstate.
|
//! by the background validation chainstate.
|
||||||
bool m_snapshot_validated{false};
|
bool m_snapshot_validated{false};
|
||||||
|
|
||||||
|
//! Internal helper for ActivateSnapshot().
|
||||||
|
[[nodiscard]] bool PopulateAndValidateSnapshot(
|
||||||
|
CChainState& snapshot_chainstate,
|
||||||
|
CAutoFile& coins_file,
|
||||||
|
const SnapshotMetadata& metadata);
|
||||||
|
|
||||||
// For access to m_active_chainstate.
|
// For access to m_active_chainstate.
|
||||||
friend CChainState& ChainstateActive();
|
friend CChainState& ChainstateActive();
|
||||||
friend CChain& ChainActive();
|
friend CChain& ChainActive();
|
||||||
@ -900,6 +908,22 @@ public:
|
|||||||
//! Get all chainstates currently being used.
|
//! Get all chainstates currently being used.
|
||||||
std::vector<CChainState*> GetAll();
|
std::vector<CChainState*> GetAll();
|
||||||
|
|
||||||
|
//! Construct and activate a Chainstate on the basis of UTXO snapshot data.
|
||||||
|
//!
|
||||||
|
//! Steps:
|
||||||
|
//!
|
||||||
|
//! - Initialize an unused CChainState.
|
||||||
|
//! - Load its `CoinsViews` contents from `coins_file`.
|
||||||
|
//! - Verify that the hash of the resulting coinsdb matches the expected hash
|
||||||
|
//! per assumeutxo chain parameters.
|
||||||
|
//! - Wait for our headers chain to include the base block of the snapshot.
|
||||||
|
//! - "Fast forward" the tip of the new chainstate to the base of the snapshot,
|
||||||
|
//! faking nTx* block index data along the way.
|
||||||
|
//! - Move the new chainstate to `m_snapshot_chainstate` and make it our
|
||||||
|
//! ChainstateActive().
|
||||||
|
[[nodiscard]] bool ActivateSnapshot(
|
||||||
|
CAutoFile& coins_file, const SnapshotMetadata& metadata, bool in_memory);
|
||||||
|
|
||||||
//! The most-work chain.
|
//! The most-work chain.
|
||||||
CChainState& ActiveChainstate() const;
|
CChainState& ActiveChainstate() const;
|
||||||
CChain& ActiveChain() const { return ActiveChainstate().m_chain; }
|
CChain& ActiveChain() const { return ActiveChainstate().m_chain; }
|
||||||
|
@ -20,6 +20,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
|||||||
"txmempool -> validation -> txmempool"
|
"txmempool -> validation -> txmempool"
|
||||||
"wallet/fees -> wallet/wallet -> wallet/fees"
|
"wallet/fees -> wallet/wallet -> wallet/fees"
|
||||||
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
||||||
|
"node/coinstats -> validation -> node/coinstats"
|
||||||
)
|
)
|
||||||
|
|
||||||
EXIT_CODE=0
|
EXIT_CODE=0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user