mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-16 13:01:19 +02:00
Merge b7e3600815 into merged_master (Bitcoin PR bitcoin/bitcoin#21526)
This commit is contained in:
commit
070c52dcef
10 changed files with 314 additions and 120 deletions
|
|
@ -9,6 +9,7 @@ EXTRA_LIBRARIES += \
|
|||
|
||||
TEST_UTIL_H = \
|
||||
test/util/blockfilter.h \
|
||||
test/util/chainstate.h \
|
||||
test/util/logging.h \
|
||||
test/util/mining.h \
|
||||
test/util/net.h \
|
||||
|
|
|
|||
24
src/chain.h
24
src/chain.h
|
|
@ -126,7 +126,15 @@ enum BlockStatus: uint32_t {
|
|||
BLOCK_FAILED_CHILD = 64, //!< descends from failed block
|
||||
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
|
||||
|
||||
BLOCK_OPT_WITNESS = 128, //!< block data in blk*.data was received with a witness-enforcing client
|
||||
BLOCK_OPT_WITNESS = 128, //!< block data in blk*.dat was received with a witness-enforcing client
|
||||
|
||||
/**
|
||||
* If set, this indicates that the block index entry is assumed-valid.
|
||||
* Certain diagnostics will be skipped in e.g. CheckBlockIndex().
|
||||
* It almost certainly means that the block's full validation is pending
|
||||
* on a background chainstate. See `doc/assumeutxo.md`.
|
||||
*/
|
||||
BLOCK_ASSUMED_VALID = 256,
|
||||
};
|
||||
|
||||
/** The block chain is a tree shaped structure starting with the
|
||||
|
|
@ -355,14 +363,24 @@ public:
|
|||
return ((nStatus & BLOCK_VALID_MASK) >= nUpTo);
|
||||
}
|
||||
|
||||
//! @returns true if the block is assumed-valid; this means it is queued to be
|
||||
//! validated by a background chainstate.
|
||||
bool IsAssumedValid() const { return nStatus & BLOCK_ASSUMED_VALID; }
|
||||
|
||||
//! Raise the validity level of this block index entry.
|
||||
//! Returns true if the validity was changed.
|
||||
bool RaiseValidity(enum BlockStatus nUpTo)
|
||||
{
|
||||
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
|
||||
if (nStatus & BLOCK_FAILED_MASK)
|
||||
return false;
|
||||
if (nStatus & BLOCK_FAILED_MASK) return false;
|
||||
|
||||
if ((nStatus & BLOCK_VALID_MASK) < nUpTo) {
|
||||
// If this block had been marked assumed-valid and we're raising
|
||||
// its validity to a certain point, there is no longer an assumption.
|
||||
if (nStatus & BLOCK_ASSUMED_VALID && nUpTo >= BLOCK_VALID_SCRIPTS) {
|
||||
nStatus &= ~BLOCK_ASSUMED_VALID;
|
||||
}
|
||||
|
||||
nStatus = (nStatus & ~BLOCK_VALID_MASK) | nUpTo;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
54
src/test/util/chainstate.h
Normal file
54
src/test/util/chainstate.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2021 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
//
|
||||
#ifndef BITCOIN_TEST_UTIL_CHAINSTATE_H
|
||||
#define BITCOIN_TEST_UTIL_CHAINSTATE_H
|
||||
|
||||
#include <clientversion.h>
|
||||
#include <fs.h>
|
||||
#include <node/context.h>
|
||||
#include <node/utxo_snapshot.h>
|
||||
#include <rpc/blockchain.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <univalue.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
const auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
|
||||
|
||||
/**
|
||||
* Create and activate a UTXO snapshot, optionally providing a function to
|
||||
* malleate the snapshot.
|
||||
*/
|
||||
template<typename F = decltype(NoMalleation)>
|
||||
static bool
|
||||
CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
|
||||
{
|
||||
// Write out a snapshot to the test's tempdir.
|
||||
//
|
||||
int height;
|
||||
WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
|
||||
fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
|
||||
FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
|
||||
CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
|
||||
|
||||
UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
|
||||
BOOST_TEST_MESSAGE(
|
||||
"Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
|
||||
|
||||
// Read the written snapshot in and then activate it.
|
||||
//
|
||||
FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
|
||||
CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
|
||||
SnapshotMetadata metadata;
|
||||
auto_infile >> metadata;
|
||||
|
||||
malleation(auto_infile, metadata);
|
||||
|
||||
return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
|
||||
}
|
||||
|
||||
|
||||
#endif // BITCOIN_TEST_UTIL_CHAINSTATE_H
|
||||
|
|
@ -259,11 +259,14 @@ void TestChain100Setup::mineBlocks(int num_blocks)
|
|||
}
|
||||
}
|
||||
|
||||
CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
|
||||
CBlock TestChain100Setup::CreateBlock(
|
||||
const std::vector<CMutableTransaction>& txns,
|
||||
const CScript& scriptPubKey,
|
||||
CChainState& chainstate)
|
||||
{
|
||||
const CChainParams& chainparams = Params();
|
||||
CTxMemPool empty_pool;
|
||||
CBlock block = BlockAssembler(m_node.chainman->ActiveChainstate(), empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
|
||||
CBlock block = BlockAssembler(chainstate, empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
|
||||
|
||||
Assert(block.vtx.size() == 1);
|
||||
for (const CMutableTransaction& tx : txns) {
|
||||
|
|
@ -273,6 +276,20 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransa
|
|||
|
||||
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
CBlock TestChain100Setup::CreateAndProcessBlock(
|
||||
const std::vector<CMutableTransaction>& txns,
|
||||
const CScript& scriptPubKey,
|
||||
CChainState* chainstate)
|
||||
{
|
||||
if (!chainstate) {
|
||||
chainstate = &Assert(m_node.chainman)->ActiveChainstate();
|
||||
}
|
||||
|
||||
const CChainParams& chainparams = Params();
|
||||
const CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
|
||||
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
|
||||
Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
|
||||
|
||||
|
|
|
|||
|
|
@ -119,9 +119,20 @@ struct TestChain100Setup : public RegTestingSetup {
|
|||
/**
|
||||
* Create a new block with just given transactions, coinbase paying to
|
||||
* scriptPubKey, and try to add it to the current chain.
|
||||
* If no chainstate is specified, default to the active.
|
||||
*/
|
||||
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
|
||||
const CScript& scriptPubKey);
|
||||
const CScript& scriptPubKey,
|
||||
CChainState* chainstate = nullptr);
|
||||
|
||||
/**
|
||||
* Create a new block with just given transactions, coinbase paying to
|
||||
* scriptPubKey.
|
||||
*/
|
||||
CBlock CreateBlock(
|
||||
const std::vector<CMutableTransaction>& txns,
|
||||
const CScript& scriptPubKey,
|
||||
CChainState& chainstate);
|
||||
|
||||
//! Mine a series of new blocks on the active chain.
|
||||
void mineBlocks(int num_blocks);
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
//
|
||||
#include <chainparams.h>
|
||||
#include <random.h>
|
||||
#include <uint256.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <sync.h>
|
||||
#include <rpc/blockchain.h>
|
||||
#include <test/util/chainstate.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <validation.h>
|
||||
|
||||
|
|
@ -73,4 +76,77 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
|
|||
WITH_LOCK(::cs_main, manager.Unload());
|
||||
}
|
||||
|
||||
//! Test UpdateTip behavior for both active and background chainstates.
|
||||
//!
|
||||
//! When run on the background chainstate, UpdateTip should do a subset
|
||||
//! of what it does for the active chainstate.
|
||||
BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
|
||||
{
|
||||
ChainstateManager& chainman = *Assert(m_node.chainman);
|
||||
uint256 curr_tip = ::g_best_block;
|
||||
|
||||
// Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
|
||||
// be found.
|
||||
mineBlocks(10);
|
||||
|
||||
// After adding some blocks to the tip, best block should have changed.
|
||||
BOOST_CHECK(::g_best_block != curr_tip);
|
||||
|
||||
BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
|
||||
|
||||
// Ensure our active chain is the snapshot chainstate.
|
||||
BOOST_CHECK(chainman.IsSnapshotActive());
|
||||
|
||||
curr_tip = ::g_best_block;
|
||||
|
||||
// Mine a new block on top of the activated snapshot chainstate.
|
||||
mineBlocks(1); // Defined in TestChain100Setup.
|
||||
|
||||
// After adding some blocks to the snapshot tip, best block should have changed.
|
||||
BOOST_CHECK(::g_best_block != curr_tip);
|
||||
|
||||
curr_tip = ::g_best_block;
|
||||
|
||||
CChainState* background_cs;
|
||||
|
||||
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 2);
|
||||
for (CChainState* cs : chainman.GetAll()) {
|
||||
if (cs != &chainman.ActiveChainstate()) {
|
||||
background_cs = cs;
|
||||
}
|
||||
}
|
||||
BOOST_CHECK(background_cs);
|
||||
|
||||
// Create a block to append to the validation chain.
|
||||
std::vector<CMutableTransaction> noTxns;
|
||||
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
|
||||
CBlock validation_block = this->CreateBlock(noTxns, scriptPubKey, *background_cs);
|
||||
auto pblock = std::make_shared<const CBlock>(validation_block);
|
||||
BlockValidationState state;
|
||||
CBlockIndex* pindex = nullptr;
|
||||
const CChainParams& chainparams = Params();
|
||||
bool newblock = false;
|
||||
|
||||
// TODO: much of this is inlined from ProcessNewBlock(); just reuse PNB()
|
||||
// once it is changed to support multiple chainstates.
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
|
||||
BOOST_CHECK(checked);
|
||||
bool accepted = background_cs->AcceptBlock(
|
||||
pblock, state, &pindex, true, nullptr, &newblock);
|
||||
BOOST_CHECK(accepted);
|
||||
}
|
||||
// UpdateTip is called here
|
||||
bool block_added = background_cs->ActivateBestChain(state, pblock);
|
||||
|
||||
// Ensure tip is as expected
|
||||
BOOST_CHECK_EQUAL(background_cs->m_chain.Tip()->GetBlockHash(), validation_block.GetHash());
|
||||
|
||||
// g_best_block should be unchanged after adding a block to the background
|
||||
// validation chain.
|
||||
BOOST_CHECK(block_added);
|
||||
BOOST_CHECK_EQUAL(curr_tip, ::g_best_block);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
#include <random.h>
|
||||
#include <rpc/blockchain.h>
|
||||
#include <sync.h>
|
||||
#include <test/util/chainstate.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <uint256.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
|
||||
#include <tinyformat.h>
|
||||
#include <univalue.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -44,7 +44,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
|
|||
|
||||
BOOST_CHECK(!manager.IsSnapshotActive());
|
||||
BOOST_CHECK(!manager.IsSnapshotValidated());
|
||||
BOOST_CHECK(!manager.IsBackgroundIBD(&c1));
|
||||
auto all = manager.GetAll();
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(all.begin(), all.end(), chainstates.begin(), chainstates.end());
|
||||
|
||||
|
|
@ -57,9 +56,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
|
|||
auto exp_tip = c1.m_chain.Tip();
|
||||
BOOST_CHECK_EQUAL(active_tip, exp_tip);
|
||||
|
||||
auto& validated_cs = manager.ValidatedChainstate();
|
||||
BOOST_CHECK_EQUAL(&validated_cs, &c1);
|
||||
|
||||
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
|
||||
|
||||
// Create a snapshot-based chainstate.
|
||||
|
|
@ -81,8 +77,8 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
|
|||
|
||||
BOOST_CHECK(manager.IsSnapshotActive());
|
||||
BOOST_CHECK(!manager.IsSnapshotValidated());
|
||||
BOOST_CHECK(manager.IsBackgroundIBD(&c1));
|
||||
BOOST_CHECK(!manager.IsBackgroundIBD(&c2));
|
||||
BOOST_CHECK_EQUAL(&c2, &manager.ActiveChainstate());
|
||||
BOOST_CHECK(&c1 != &manager.ActiveChainstate());
|
||||
auto all2 = manager.GetAll();
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(all2.begin(), all2.end(), chainstates.begin(), chainstates.end());
|
||||
|
||||
|
|
@ -99,16 +95,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
|
|||
// CCoinsViewCache instances.
|
||||
BOOST_CHECK(exp_tip != exp_tip2);
|
||||
|
||||
auto& validated_cs2 = manager.ValidatedChainstate();
|
||||
BOOST_CHECK_EQUAL(&validated_cs2, &c1);
|
||||
|
||||
auto& validated_chain = manager.ValidatedChain();
|
||||
BOOST_CHECK_EQUAL(&validated_chain, &c1.m_chain);
|
||||
|
||||
auto validated_tip = manager.ValidatedTip();
|
||||
exp_tip = c1.m_chain.Tip();
|
||||
BOOST_CHECK_EQUAL(validated_tip, exp_tip);
|
||||
|
||||
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded
|
||||
SyncWithValidationInterfaceQueue();
|
||||
|
||||
|
|
@ -168,36 +154,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
|
|||
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
|
||||
}
|
||||
|
||||
auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
|
||||
|
||||
template<typename F = decltype(NoMalleation)>
|
||||
static bool
|
||||
CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
|
||||
{
|
||||
// Write out a snapshot to the test's tempdir.
|
||||
//
|
||||
int height;
|
||||
WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
|
||||
fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
|
||||
FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
|
||||
CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
|
||||
|
||||
UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
|
||||
BOOST_TEST_MESSAGE(
|
||||
"Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
|
||||
|
||||
// Read the written snapshot in and then activate it.
|
||||
//
|
||||
FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
|
||||
CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
|
||||
SnapshotMetadata metadata;
|
||||
auto_infile >> metadata;
|
||||
|
||||
malleation(auto_infile, metadata);
|
||||
|
||||
return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
|
||||
}
|
||||
|
||||
//! Test basic snapshot activation.
|
||||
BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
|
||||
{
|
||||
|
|
@ -321,27 +277,27 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
|
|||
{
|
||||
LOCK(::cs_main);
|
||||
size_t coins_in_active{0};
|
||||
size_t coins_in_ibd{0};
|
||||
size_t coins_missing_ibd{0};
|
||||
size_t coins_in_background{0};
|
||||
size_t coins_missing_from_background{0};
|
||||
|
||||
for (CChainState* chainstate : chainman.GetAll()) {
|
||||
BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
|
||||
CCoinsViewCache& coinscache = chainstate->CoinsTip();
|
||||
bool is_ibd = chainman.IsBackgroundIBD(chainstate);
|
||||
bool is_background = chainstate != &chainman.ActiveChainstate();
|
||||
|
||||
for (CTransactionRef& txn : m_coinbase_txns) {
|
||||
COutPoint op{txn->GetHash(), 0};
|
||||
if (coinscache.HaveCoin(op)) {
|
||||
(is_ibd ? coins_in_ibd : coins_in_active)++;
|
||||
} else if (is_ibd) {
|
||||
coins_missing_ibd++;
|
||||
(is_background ? coins_in_background : coins_in_active)++;
|
||||
} else if (is_background) {
|
||||
coins_missing_from_background++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);
|
||||
BOOST_CHECK_EQUAL(coins_in_ibd, initial_total_coins);
|
||||
BOOST_CHECK_EQUAL(coins_missing_ibd, new_coins);
|
||||
BOOST_CHECK_EQUAL(coins_in_background, initial_total_coins);
|
||||
BOOST_CHECK_EQUAL(coins_missing_from_background, new_coins);
|
||||
}
|
||||
|
||||
// Snapshot should refuse to load after one has already loaded.
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, BasicTestingSetup)
|
||||
BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, ChainTestingSetup)
|
||||
|
||||
//! Test utilities for detecting when we need to flush the coins cache based
|
||||
//! on estimated memory usage.
|
||||
|
|
@ -20,7 +20,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
|
|||
{
|
||||
CTxMemPool mempool;
|
||||
BlockManager blockman{};
|
||||
CChainState chainstate{&mempool, blockman};
|
||||
CChainState chainstate{&mempool, blockman, *Assert(m_node.chainman)};
|
||||
chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
|
||||
WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
|
||||
|
||||
|
|
|
|||
|
|
@ -1185,10 +1185,15 @@ void CoinsViews::InitCache()
|
|||
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
|
||||
}
|
||||
|
||||
CChainState::CChainState(CTxMemPool* mempool, BlockManager& blockman, std::optional<uint256> from_snapshot_blockhash)
|
||||
CChainState::CChainState(
|
||||
CTxMemPool* mempool,
|
||||
BlockManager& blockman,
|
||||
ChainstateManager& chainman,
|
||||
std::optional<uint256> from_snapshot_blockhash)
|
||||
: m_mempool(mempool),
|
||||
m_params(::Params()),
|
||||
m_blockman(blockman),
|
||||
m_chainman(chainman),
|
||||
m_from_snapshot_blockhash(from_snapshot_blockhash) {}
|
||||
|
||||
void CChainState::InitCoinsDB(
|
||||
|
|
@ -2340,8 +2345,42 @@ static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
|
|||
res += warn;
|
||||
}
|
||||
|
||||
static void UpdateTipLog(
|
||||
const CCoinsViewCache& coins_tip,
|
||||
const CBlockIndex* tip,
|
||||
const CChainParams& params,
|
||||
const std::string& func_name,
|
||||
const std::string& prefix,
|
||||
const std::string& warning_messages) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
|
||||
AssertLockHeld(::cs_main);
|
||||
LogPrintf("%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n",
|
||||
prefix, func_name,
|
||||
tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion,
|
||||
log(tip->nChainWork.getdouble()) / log(2.0), (unsigned long)tip->nChainTx,
|
||||
FormatISO8601DateTime(tip->GetBlockTime()),
|
||||
GuessVerificationProgress(tip, params.GetConsensus().nPowTargetSpacing),
|
||||
coins_tip.DynamicMemoryUsage() * (1.0 / (1 << 20)),
|
||||
coins_tip.GetCacheSize(),
|
||||
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages) : "");
|
||||
}
|
||||
|
||||
void CChainState::UpdateTip(const CBlockIndex* pindexNew)
|
||||
{
|
||||
const auto& coins_tip = this->CoinsTip();
|
||||
|
||||
// The remainder of the function isn't relevant if we are not acting on
|
||||
// the active chainstate, so return if need be.
|
||||
if (this != &m_chainman.ActiveChainstate()) {
|
||||
// Only log every so often so that we don't bury log messages at the tip.
|
||||
constexpr int BACKGROUND_LOG_INTERVAL = 2000;
|
||||
if (pindexNew->nHeight % BACKGROUND_LOG_INTERVAL == 0) {
|
||||
UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "[background validation] ", "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// New best block
|
||||
if (m_mempool) {
|
||||
m_mempool->AddTransactionsUpdated(1);
|
||||
|
|
@ -2369,13 +2408,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
|
|||
}
|
||||
}
|
||||
}
|
||||
LogPrintf("%s: new best=%s height=%d version=0x%08x tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
|
||||
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
|
||||
(unsigned long)pindexNew->nChainTx,
|
||||
FormatISO8601DateTime(pindexNew->GetBlockTime()),
|
||||
GuessVerificationProgress(pindexNew, m_params.GetConsensus().nPowTargetSpacing),
|
||||
this->CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), this->CoinsTip().GetCacheSize(),
|
||||
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
|
||||
UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "", warning_messages.original);
|
||||
|
||||
// Do some logging if dynafed parameters changed.
|
||||
if (pindexNew->pprev && !pindexNew->dynafed_params().IsNull()) {
|
||||
|
|
@ -4067,7 +4100,9 @@ bool BlockManager::LoadBlockIndex(
|
|||
pindex->nStatus |= BLOCK_FAILED_CHILD;
|
||||
setDirtyBlockIndex.insert(pindex);
|
||||
}
|
||||
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr)) {
|
||||
if (pindex->IsAssumedValid() ||
|
||||
(pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
|
||||
(pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
|
||||
block_index_candidates.insert(pindex);
|
||||
}
|
||||
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
|
||||
|
|
@ -4647,12 +4682,33 @@ void CChainState::CheckBlockIndex()
|
|||
while (pindex != nullptr) {
|
||||
nNodes++;
|
||||
if (pindexFirstInvalid == nullptr && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex;
|
||||
if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex;
|
||||
// Assumed-valid index entries will not have data since we haven't downloaded the
|
||||
// full block yet.
|
||||
if (pindexFirstMissing == nullptr && !(pindex->nStatus & BLOCK_HAVE_DATA) && !pindex->IsAssumedValid()) {
|
||||
pindexFirstMissing = pindex;
|
||||
}
|
||||
if (pindexFirstNeverProcessed == nullptr && pindex->nTx == 0) pindexFirstNeverProcessed = pindex;
|
||||
if (pindex->pprev != nullptr && pindexFirstNotTreeValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex;
|
||||
if (pindex->pprev != nullptr && pindexFirstNotTransactionsValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex;
|
||||
if (pindex->pprev != nullptr && pindexFirstNotChainValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex;
|
||||
if (pindex->pprev != nullptr && pindexFirstNotScriptsValid == nullptr && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex;
|
||||
|
||||
if (pindex->pprev != nullptr && !pindex->IsAssumedValid()) {
|
||||
// Skip validity flag checks for BLOCK_ASSUMED_VALID index entries, since these
|
||||
// *_VALID_MASK flags will not be present for index entries we are temporarily assuming
|
||||
// valid.
|
||||
if (pindexFirstNotTransactionsValid == nullptr &&
|
||||
(pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) {
|
||||
pindexFirstNotTransactionsValid = pindex;
|
||||
}
|
||||
|
||||
if (pindexFirstNotChainValid == nullptr &&
|
||||
(pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) {
|
||||
pindexFirstNotChainValid = pindex;
|
||||
}
|
||||
|
||||
if (pindexFirstNotScriptsValid == nullptr &&
|
||||
(pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) {
|
||||
pindexFirstNotScriptsValid = pindex;
|
||||
}
|
||||
}
|
||||
|
||||
// Begin: actual consistency checks.
|
||||
if (pindex->pprev == nullptr) {
|
||||
|
|
@ -4663,7 +4719,9 @@ void CChainState::CheckBlockIndex()
|
|||
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
|
||||
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
|
||||
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
|
||||
if (!fHavePruned) {
|
||||
// Unless these indexes are assumed valid and pending block download on a
|
||||
// background chainstate.
|
||||
if (!fHavePruned && !pindex->IsAssumedValid()) {
|
||||
// If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0
|
||||
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
|
||||
assert(pindexFirstMissing == pindexFirstNeverProcessed);
|
||||
|
|
@ -4672,7 +4730,16 @@ void CChainState::CheckBlockIndex()
|
|||
if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0);
|
||||
}
|
||||
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
|
||||
assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
|
||||
if (pindex->IsAssumedValid()) {
|
||||
// Assumed-valid blocks should have some nTx value.
|
||||
assert(pindex->nTx > 0);
|
||||
// Assumed-valid blocks should connect to the main chain.
|
||||
assert((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE);
|
||||
} else {
|
||||
// Otherwise there should only be an nTx value if we have
|
||||
// actually seen a block's transactions.
|
||||
assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
|
||||
}
|
||||
// All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded().
|
||||
assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
|
||||
assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
|
||||
|
|
@ -4689,11 +4756,17 @@ void CChainState::CheckBlockIndex()
|
|||
}
|
||||
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && pindexFirstNeverProcessed == nullptr) {
|
||||
if (pindexFirstInvalid == nullptr) {
|
||||
const bool is_active = this == &m_chainman.ActiveChainstate();
|
||||
|
||||
// If this block sorts at least as good as the current tip and
|
||||
// is valid and we have all data for its parents, it must be in
|
||||
// setBlockIndexCandidates. m_chain.Tip() must also be there
|
||||
// even if some data has been pruned.
|
||||
if (pindexFirstMissing == nullptr || pindex == m_chain.Tip()) {
|
||||
//
|
||||
// Don't perform this check for the background chainstate since
|
||||
// its setBlockIndexCandidates shouldn't have some entries (i.e. those past the
|
||||
// snapshot block) which do exist in the block index for the active chainstate.
|
||||
if (is_active && (pindexFirstMissing == nullptr || pindex == m_chain.Tip())) {
|
||||
assert(setBlockIndexCandidates.count(pindex));
|
||||
}
|
||||
// If some parent is missing, then it could be that this block was in
|
||||
|
|
@ -5027,7 +5100,7 @@ CChainState& ChainstateManager::InitializeChainstate(
|
|||
if (to_modify) {
|
||||
throw std::logic_error("should not be overwriting a chainstate");
|
||||
}
|
||||
to_modify.reset(new CChainState(mempool, m_blockman, snapshot_blockhash));
|
||||
to_modify.reset(new CChainState(mempool, m_blockman, *this, snapshot_blockhash));
|
||||
|
||||
// Snapshot chainstates and initial IBD chaintates always become active.
|
||||
if (is_snapshot || (!is_snapshot && !m_active_chainstate)) {
|
||||
|
|
@ -5096,8 +5169,9 @@ bool ChainstateManager::ActivateSnapshot(
|
|||
static_cast<size_t>(current_coinsdb_cache_size * IBD_CACHE_PERC));
|
||||
}
|
||||
|
||||
auto snapshot_chainstate = WITH_LOCK(::cs_main, return std::make_unique<CChainState>(
|
||||
/* mempool */ nullptr, m_blockman, base_blockhash));
|
||||
auto snapshot_chainstate = WITH_LOCK(::cs_main,
|
||||
return std::make_unique<CChainState>(
|
||||
/* mempool */ nullptr, m_blockman, *this, base_blockhash));
|
||||
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
|
|
@ -5300,11 +5374,25 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
|
|||
// Fake nChainTx so that GuessVerificationProgress reports accurately
|
||||
index->nChainTx = index->pprev ? index->pprev->nChainTx + index->nTx : 1;
|
||||
|
||||
// Mark unvalidated block index entries beneath the snapshot base block as assumed-valid.
|
||||
if (!index->IsValid(BLOCK_VALID_SCRIPTS)) {
|
||||
// This flag will be removed once the block is fully validated by a
|
||||
// background chainstate.
|
||||
index->nStatus |= BLOCK_ASSUMED_VALID;
|
||||
}
|
||||
|
||||
// Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
|
||||
// won't ask to rewind the entire assumed-valid chain on startup.
|
||||
if (index->pprev && DeploymentActiveAt(*index, ::Params().GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) {
|
||||
index->nStatus |= BLOCK_OPT_WITNESS;
|
||||
}
|
||||
|
||||
setDirtyBlockIndex.insert(index);
|
||||
// Changes to the block index will be flushed to disk after this call
|
||||
// returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is
|
||||
// called, since we've added a snapshot chainstate and therefore will
|
||||
// have to downsize the IBD chainstate, which will result in a call to
|
||||
// `FlushStateToDisk(ALWAYS)`.
|
||||
}
|
||||
|
||||
assert(index);
|
||||
|
|
@ -5329,22 +5417,6 @@ bool ChainstateManager::IsSnapshotActive() const
|
|||
return m_snapshot_chainstate && m_active_chainstate == m_snapshot_chainstate.get();
|
||||
}
|
||||
|
||||
CChainState& ChainstateManager::ValidatedChainstate() const
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
if (m_snapshot_chainstate && IsSnapshotValidated()) {
|
||||
return *m_snapshot_chainstate.get();
|
||||
}
|
||||
assert(m_ibd_chainstate);
|
||||
return *m_ibd_chainstate.get();
|
||||
}
|
||||
|
||||
bool ChainstateManager::IsBackgroundIBD(CChainState* chainstate) const
|
||||
{
|
||||
LOCK(::cs_main);
|
||||
return (m_snapshot_chainstate && chainstate == m_ibd_chainstate.get());
|
||||
}
|
||||
|
||||
void ChainstateManager::Unload()
|
||||
{
|
||||
for (CChainState* chainstate : this->GetAll()) {
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ extern RecursiveMutex cs_main;
|
|||
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
||||
extern Mutex g_best_block_mutex;
|
||||
extern std::condition_variable g_best_block_cv;
|
||||
/** Used to notify getblocktemplate RPC of new tips. */
|
||||
extern uint256 g_best_block;
|
||||
/** Whether there are dedicated script-checking threads running.
|
||||
* False indicates all script checking is done on the main threadMessageHandler thread.
|
||||
|
|
@ -584,9 +585,15 @@ public:
|
|||
//! CChainState instances.
|
||||
BlockManager& m_blockman;
|
||||
|
||||
//! The chainstate manager that owns this chainstate. The reference is
|
||||
//! necessary so that this instance can check whether it is the active
|
||||
//! chainstate within deeply nested method calls.
|
||||
ChainstateManager& m_chainman;
|
||||
|
||||
explicit CChainState(
|
||||
CTxMemPool* mempool,
|
||||
BlockManager& blockman,
|
||||
ChainstateManager& chainman,
|
||||
std::optional<uint256> from_snapshot_blockhash = std::nullopt);
|
||||
|
||||
/**
|
||||
|
|
@ -623,9 +630,10 @@ public:
|
|||
const std::optional<uint256> m_from_snapshot_blockhash;
|
||||
|
||||
/**
|
||||
* The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and
|
||||
* as good as our current tip or better. Entries may be failed, though, and pruning nodes may be
|
||||
* missing the data for the block.
|
||||
* The set of all CBlockIndex entries with either BLOCK_VALID_TRANSACTIONS (for
|
||||
* itself and all ancestors) *or* BLOCK_ASSUMED_VALID (if using background
|
||||
* chainstates) and as good as our current tip or better. Entries may be failed,
|
||||
* though, and pruning nodes may be missing the data for the block.
|
||||
*/
|
||||
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
|
||||
|
||||
|
|
@ -838,12 +846,6 @@ bool ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostW
|
|||
* *Background IBD chainstate*: an IBD chainstate for which the
|
||||
* IBD process is happening in the background while use of the
|
||||
* active (snapshot) chainstate allows the rest of the system to function.
|
||||
*
|
||||
* *Validated chainstate*: the most-work chainstate which has been validated
|
||||
* locally via initial block download. This will be the snapshot chainstate
|
||||
* if a snapshot was loaded and all blocks up to the snapshot starting point
|
||||
* have been downloaded and validated (via background validation), otherwise
|
||||
* it will be the IBD chainstate.
|
||||
*/
|
||||
class ChainstateManager
|
||||
{
|
||||
|
|
@ -962,19 +964,6 @@ public:
|
|||
//! Is there a snapshot in use and has it been fully validated?
|
||||
bool IsSnapshotValidated() const { return m_snapshot_validated; }
|
||||
|
||||
//! @returns true if this chainstate is being used to validate an active
|
||||
//! snapshot in the background.
|
||||
bool IsBackgroundIBD(CChainState* chainstate) const;
|
||||
|
||||
//! Return the most-work chainstate that has been fully validated.
|
||||
//!
|
||||
//! During background validation of a snapshot, this is the IBD chain. After
|
||||
//! background validation has completed, this is the snapshot chain.
|
||||
CChainState& ValidatedChainstate() const;
|
||||
|
||||
CChain& ValidatedChain() const { return ValidatedChainstate().m_chain; }
|
||||
CBlockIndex* ValidatedTip() const { return ValidatedChain().Tip(); }
|
||||
|
||||
/**
|
||||
* Process an incoming block. This only returns after the best known valid
|
||||
* block is made active. Note that it does not, however, guarantee that the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue