validation: rename snapshot chainstate dir
This changes the snapshot's leveldb chainstate dir name from `chainstate_[blockhash]` to `chainstate_snapshot`. This simplifies later logic that loads snapshot data, and enforces the limitation of a single snapshot at any given time. Since we still need to persis the blockhash of the base block, we write that out to a file (`chainstate_snapshot/base_blockhash`) for later use during initialization, so that we can reinitialize the snapshot chainstate. Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
This commit is contained in:
1 parent
d14bebf100
commit
f9f1735f13
7 files changed
+133
-5
No files matched your search
@@ -395,6 +395,7 @@ libbitcoin_node_a_SOURCES = \
|
|||||||
node/minisketchwrapper.cpp \
|
node/minisketchwrapper.cpp \
|
||||||
node/psbt.cpp \
|
node/psbt.cpp \
|
||||||
node/transaction.cpp \
|
node/transaction.cpp \
|
||||||
|
node/utxo_snapshot.cpp \
|
||||||
node/validation_cache_args.cpp \
|
node/validation_cache_args.cpp \
|
||||||
noui.cpp \
|
noui.cpp \
|
||||||
policy/fees.cpp \
|
policy/fees.cpp \
|
||||||
@@ -900,6 +901,7 @@ libbitcoinkernel_la_SOURCES = \
|
|||||||
node/blockstorage.cpp \
|
node/blockstorage.cpp \
|
||||||
node/chainstate.cpp \
|
node/chainstate.cpp \
|
||||||
node/interface_ui.cpp \
|
node/interface_ui.cpp \
|
||||||
|
node/utxo_snapshot.cpp \
|
||||||
policy/feerate.cpp \
|
policy/feerate.cpp \
|
||||||
policy/fees.cpp \
|
policy/fees.cpp \
|
||||||
policy/packages.cpp \
|
policy/packages.cpp \
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) 2022 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <node/utxo_snapshot.h>
|
||||||
|
|
||||||
|
#include <fs.h>
|
||||||
|
#include <logging.h>
|
||||||
|
#include <streams.h>
|
||||||
|
#include <uint256.h>
|
||||||
|
#include <validation.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
|
||||||
|
bool WriteSnapshotBaseBlockhash(Chainstate& snapshot_chainstate)
|
||||||
|
{
|
||||||
|
AssertLockHeld(::cs_main);
|
||||||
|
assert(snapshot_chainstate.m_from_snapshot_blockhash);
|
||||||
|
|
||||||
|
const std::optional<fs::path> chaindir = snapshot_chainstate.CoinsDB().StoragePath();
|
||||||
|
assert(chaindir); // Sanity check that chainstate isn't in-memory.
|
||||||
|
const fs::path write_to = *chaindir / node::SNAPSHOT_BLOCKHASH_FILENAME;
|
||||||
|
|
||||||
|
FILE* file{fsbridge::fopen(write_to, "wb")};
|
||||||
|
AutoFile afile{file};
|
||||||
|
if (afile.IsNull()) {
|
||||||
|
LogPrintf("[snapshot] failed to open base blockhash file for writing: %s\n",
|
||||||
|
fs::PathToString(write_to));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
afile << *snapshot_chainstate.m_from_snapshot_blockhash;
|
||||||
|
|
||||||
|
if (afile.fclose() != 0) {
|
||||||
|
LogPrintf("[snapshot] failed to close base blockhash file %s after writing\n",
|
||||||
|
fs::PathToString(write_to));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
|
||||||
|
{
|
||||||
|
if (!fs::exists(chaindir)) {
|
||||||
|
LogPrintf("[snapshot] cannot read base blockhash: no chainstate dir " /* Continued */
|
||||||
|
"exists at path %s\n", fs::PathToString(chaindir));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
const fs::path read_from = chaindir / node::SNAPSHOT_BLOCKHASH_FILENAME;
|
||||||
|
const std::string read_from_str = fs::PathToString(read_from);
|
||||||
|
|
||||||
|
if (!fs::exists(read_from)) {
|
||||||
|
LogPrintf("[snapshot] snapshot chainstate dir is malformed! no base blockhash file " /* Continued */
|
||||||
|
"exists at path %s. Try deleting %s and calling loadtxoutset again?\n",
|
||||||
|
fs::PathToString(chaindir), read_from_str);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 base_blockhash;
|
||||||
|
FILE* file{fsbridge::fopen(read_from, "rb")};
|
||||||
|
AutoFile afile{file};
|
||||||
|
if (afile.IsNull()) {
|
||||||
|
LogPrintf("[snapshot] failed to open base blockhash file for reading: %s\n",
|
||||||
|
read_from_str);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
afile >> base_blockhash;
|
||||||
|
|
||||||
|
if (std::fgetc(afile.Get()) != EOF) {
|
||||||
|
LogPrintf("[snapshot] warning: unexpected trailing data in %s\n", read_from_str);
|
||||||
|
} else if (std::ferror(afile.Get())) {
|
||||||
|
LogPrintf("[snapshot] warning: i/o error reading %s\n", read_from_str);
|
||||||
|
}
|
||||||
|
return base_blockhash;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace node
|
||||||
@@ -6,8 +6,14 @@
|
|||||||
#ifndef BITCOIN_NODE_UTXO_SNAPSHOT_H
|
#ifndef BITCOIN_NODE_UTXO_SNAPSHOT_H
|
||||||
#define BITCOIN_NODE_UTXO_SNAPSHOT_H
|
#define BITCOIN_NODE_UTXO_SNAPSHOT_H
|
||||||
|
|
||||||
|
#include <fs.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
|
#include <validation.h>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
extern RecursiveMutex cs_main;
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
//! Metadata describing a serialized version of a UTXO set from which an
|
//! Metadata describing a serialized version of a UTXO set from which an
|
||||||
@@ -33,6 +39,27 @@ public:
|
|||||||
|
|
||||||
SERIALIZE_METHODS(SnapshotMetadata, obj) { READWRITE(obj.m_base_blockhash, obj.m_coins_count); }
|
SERIALIZE_METHODS(SnapshotMetadata, obj) { READWRITE(obj.m_base_blockhash, obj.m_coins_count); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! The file in the snapshot chainstate dir which stores the base blockhash. This is
|
||||||
|
//! needed to reconstruct snapshot chainstates on init.
|
||||||
|
//!
|
||||||
|
//! Because we only allow loading a single snapshot at a time, there will only be one
|
||||||
|
//! chainstate directory with this filename present within it.
|
||||||
|
const fs::path SNAPSHOT_BLOCKHASH_FILENAME{"base_blockhash"};
|
||||||
|
|
||||||
|
//! Write out the blockhash of the snapshot base block that was used to construct
|
||||||
|
//! this chainstate. This value is read in during subsequent initializations and
|
||||||
|
//! used to reconstruct snapshot-based chainstates.
|
||||||
|
bool WriteSnapshotBaseBlockhash(Chainstate& snapshot_chainstate)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
|
//! Read the blockhash of the snapshot base block that was used to construct the
|
||||||
|
//! chainstate.
|
||||||
|
std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
|
constexpr std::string_view SNAPSHOT_CHAINSTATE_SUFFIX = "_snapshot";
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H
|
#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H
|
||||||
+4
-2
@@ -488,12 +488,14 @@ public:
|
|||||||
AutoFile(const AutoFile&) = delete;
|
AutoFile(const AutoFile&) = delete;
|
||||||
AutoFile& operator=(const AutoFile&) = delete;
|
AutoFile& operator=(const AutoFile&) = delete;
|
||||||
|
|
||||||
void fclose()
|
int fclose()
|
||||||
{
|
{
|
||||||
|
int retval{0};
|
||||||
if (file) {
|
if (file) {
|
||||||
::fclose(file);
|
retval = ::fclose(file);
|
||||||
file = nullptr;
|
file = nullptr;
|
||||||
}
|
}
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get wrapped FILE* with transfer of ownership.
|
/** Get wrapped FILE* with transfer of ownership.
|
||||||
|
|||||||
@@ -232,8 +232,17 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
|
|||||||
*chainman.ActiveChainstate().m_from_snapshot_blockhash,
|
*chainman.ActiveChainstate().m_from_snapshot_blockhash,
|
||||||
*chainman.SnapshotBlockhash());
|
*chainman.SnapshotBlockhash());
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(::cs_main);
|
||||||
|
|
||||||
|
// Note: WriteSnapshotBaseBlockhash() is implicitly tested above.
|
||||||
|
BOOST_CHECK_EQUAL(
|
||||||
|
*node::ReadSnapshotBaseBlockhash(m_args.GetDataDirNet() / "chainstate_snapshot"),
|
||||||
|
*chainman.SnapshotBlockhash());
|
||||||
|
|
||||||
// Ensure that the genesis block was not marked assumed-valid.
|
// Ensure that the genesis block was not marked assumed-valid.
|
||||||
BOOST_CHECK(WITH_LOCK(::cs_main, return !chainman.ActiveChain().Genesis()->IsAssumedValid()));
|
BOOST_CHECK(!chainman.ActiveChain().Genesis()->IsAssumedValid());
|
||||||
|
}
|
||||||
|
|
||||||
const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
|
const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
|
||||||
const CBlockIndex* tip = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip());
|
const CBlockIndex* tip = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip());
|
||||||
|
|||||||
+10
-2
@@ -1515,7 +1515,7 @@ void Chainstate::InitCoinsDB(
|
|||||||
fs::path leveldb_name)
|
fs::path leveldb_name)
|
||||||
{
|
{
|
||||||
if (m_from_snapshot_blockhash) {
|
if (m_from_snapshot_blockhash) {
|
||||||
leveldb_name += "_" + m_from_snapshot_blockhash->ToString();
|
leveldb_name += node::SNAPSHOT_CHAINSTATE_SUFFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_coins_views = std::make_unique<CoinsViews>(
|
m_coins_views = std::make_unique<CoinsViews>(
|
||||||
@@ -4837,9 +4837,17 @@ bool ChainstateManager::ActivateSnapshot(
|
|||||||
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
|
static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool snapshot_ok = this->PopulateAndValidateSnapshot(
|
bool snapshot_ok = this->PopulateAndValidateSnapshot(
|
||||||
*snapshot_chainstate, coins_file, metadata);
|
*snapshot_chainstate, coins_file, metadata);
|
||||||
|
|
||||||
|
// If not in-memory, persist the base blockhash for use during subsequent
|
||||||
|
// initialization.
|
||||||
|
if (!in_memory) {
|
||||||
|
LOCK(::cs_main);
|
||||||
|
if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
|
||||||
|
snapshot_ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!snapshot_ok) {
|
if (!snapshot_ok) {
|
||||||
WITH_LOCK(::cs_main, this->MaybeRebalanceCaches());
|
WITH_LOCK(::cs_main, this->MaybeRebalanceCaches());
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import sys
|
|||||||
EXPECTED_CIRCULAR_DEPENDENCIES = (
|
EXPECTED_CIRCULAR_DEPENDENCIES = (
|
||||||
"chainparamsbase -> util/system -> chainparamsbase",
|
"chainparamsbase -> util/system -> chainparamsbase",
|
||||||
"node/blockstorage -> validation -> node/blockstorage",
|
"node/blockstorage -> validation -> node/blockstorage",
|
||||||
|
"node/utxo_snapshot -> validation -> node/utxo_snapshot",
|
||||||
"policy/fees -> txmempool -> policy/fees",
|
"policy/fees -> txmempool -> policy/fees",
|
||||||
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel",
|
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel",
|
||||||
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel",
|
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel",
|
||||||
|
|||||||
Reference in new issue
Block a user