5#include <chainparams.h>
31#include <boost/test/unit_test.hpp>
73 const uint256 snapshot_blockhash = active_tip->GetBlockHash();
79 c2.InitCoinsCache(8_MiB);
80 c2.CoinsTip().SetBestBlock(active_tip->GetBlockHash());
81 for (
const auto&
cs : manager.m_chainstates) {
82 cs->ClearBlockIndexCandidates();
85 for (
const auto&
cs : manager.m_chainstates) {
86 cs->PopulateBlockIndexCandidates();
124 auto& snapshot{
WITH_LOCK(
::cs_main,
return manager.AddChainstate(std::make_unique<Chainstate>(
nullptr, manager.m_blockman, manager,
uint256::ONE)))};
134 size_t max_cache = 10000;
138 std::vector<Chainstate*> chainstates;
143 chainstates.push_back(&c1);
146 c1.InitCoinsCache(8_MiB);
147 manager.MaybeRebalanceCaches();
157 chainstates.push_back(&c2);
172 c2.InitCoinsCache(8_MiB);
173 manager.MaybeRebalanceCaches();
178 BOOST_CHECK_CLOSE(
double(c2.m_coinstip_cache_size_bytes), max_cache * 0.95, 1);
179 BOOST_CHECK_CLOSE(
double(c2.m_coinsdb_cache_size_bytes), max_cache * 0.95, 1);
186 auto apply{[&](
bool cached_is_ibd,
bool loading_blocks,
bool tip_exists,
bool enough_work,
bool tip_recent) {
188 chainman.ResetChainstates();
191 const auto recent_time{Now<NodeSeconds>() - chainman.m_options.max_tip_age};
193 chainman.m_cached_is_ibd.store(cached_is_ibd, std::memory_order_relaxed);
194 chainman.m_blockman.m_importing = loading_blocks;
196 tip.
nChainWork = chainman.MinimumChainWork() - (enough_work ? 0 : 1);
197 tip.
nTime = (recent_time - (tip_recent ? 0h : 100h)).time_since_epoch().count();
198 chainman.ActiveChain().SetTip(tip);
200 assert(!chainman.ActiveChain().Tip());
202 chainman.UpdateIBDStatus();
205 for (
const bool cached_is_ibd : {
false,
true}) {
206 for (
const bool loading_blocks : {
false,
true}) {
207 for (
const bool tip_exists : {
false,
true}) {
208 for (
const bool enough_work : {
false,
true}) {
209 for (
const bool tip_recent : {
false,
true}) {
210 apply(cached_is_ibd, loading_blocks, tip_exists, enough_work, tip_recent);
211 const bool expected_ibd = cached_is_ibd && (loading_blocks || !tip_exists || !enough_work || !tip_recent);
230 .coins_db_in_memory =
false,
231 .block_tree_db_in_memory =
false,
248 size_t initial_total_coins{100};
255 size_t total_coins{0};
275 constexpr int snapshot_height = 110;
278 initial_total_coins += 10;
351 int chains_tested{0};
353 for (
const auto& chainstate : chainman.m_chainstates) {
354 BOOST_TEST_MESSAGE(
"Checking coins in " << chainstate->ToString());
360 size_t total_coins{0};
377 constexpr size_t new_coins{100};
378 mineBlocks(new_coins);
382 size_t coins_in_active{0};
383 size_t coins_in_background{0};
384 size_t coins_missing_from_background{0};
386 for (
const auto& chainstate : chainman.m_chainstates) {
387 BOOST_TEST_MESSAGE(
"Checking coins in " << chainstate->ToString());
394 (is_background ? coins_in_background : coins_in_active)++;
395 }
else if (is_background) {
396 coins_missing_from_background++;
412 loaded_snapshot_blockhash);
413 return std::make_tuple(&validation_chainstate, &snapshot_chainstate);
424 BOOST_TEST_MESSAGE(
"Simulating node restart");
427 for (
const auto&
cs : chainman.m_chainstates) {
428 if (
cs->CanFlushToDisk())
cs->ForceFlushStateToDisk();
435 chainman.ResetChainstates();
444 const BlockManager::Options blockman_opts{
445 .chainparams = chainman_opts.chainparams,
446 .blocks_dir = m_args.GetBlocksDirPath(),
447 .notifications = chainman_opts.notifications,
450 .cache_bytes = m_kernel_cache_sizes.block_tree_db,
451 .memory_only = m_block_tree_db_in_memory,
466 this->SetupSnapshot();
487 const int expected_assumed_valid{20};
488 const int last_assumed_valid_idx{111};
489 const int assumed_valid_start_idx = last_assumed_valid_idx - expected_assumed_valid;
500 auto reload_all_block_indexes = [&]() {
506 for (
const auto&
cs : chainman.m_chainstates) {
507 cs->ClearBlockIndexCandidates();
511 for (
const auto&
cs : chainman.m_chainstates) {
512 cs->PopulateBlockIndexCandidates();
518 reload_all_block_indexes();
527 if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
530 index->m_chain_tx_count = 0;
536 if (i == (assumed_valid_start_idx - 1)) {
537 validated_tip = index;
540 if (i == last_assumed_valid_idx - 1) {
541 assumed_base = index;
552 cs2.m_chain.SetTip(*assumed_base);
562 reload_all_block_indexes();
603 BOOST_CHECK_EQUAL(cs2.setBlockIndexCandidates.size(), num_indexes - last_assumed_valid_idx + 1);
616 auto* parent{child->pprev};
617 auto* grand_parent{parent->pprev};
620 child->nStatus = (child->nStatus & ~BLOCK_FAILED_VALID);
655 BOOST_REQUIRE(chainstate.InvalidateBlock(state, block99));
658 for (
int i = 0; i < 2; ++i) {
659 CreateAndProcessBlock({}, coinbase_script);
666 BOOST_REQUIRE(fork_block99->
pprev == block98);
668 BOOST_REQUIRE(fork_block100->
pprev == fork_block99);
674 chainman.RecalculateBestHeader();
676 chainstate.ActivateBestChain(state);
688 BOOST_REQUIRE(chainstate.InvalidateBlock(state, block98));
706 chainman.RecalculateBestHeader();
708 chainstate.ActivateBestChain(state);
726 this->SetupSnapshot();
756 BOOST_TEST_MESSAGE(
"Performing Load/Verify/Activate of chainstate");
759 this->LoadVerifyActivateChainstate();
780 "Ensure we can mine blocks on top of the initialized snapshot chainstate");
797 this->SetupSnapshot();
816 res =
WITH_LOCK(
::cs_main,
return chainman.MaybeValidateSnapshot(validated_cs, active_cs));
829 res =
WITH_LOCK(
::cs_main,
return chainman.MaybeValidateSnapshot(validated_cs, active_cs));
833 fs::path snapshot_invalid_dir =
gArgs.
GetDataDirNet() /
"chainstate_snapshot_INVALID";
843 BOOST_TEST_MESSAGE(
"Performing Load/Verify/Activate of chainstate");
847 this->LoadVerifyActivateChainstate();
867 "Ensure we can mine blocks on top of the \"new\" IBD chainstate");
877 auto chainstates = this->SetupSnapshot();
878 Chainstate& validation_chainstate = *std::get<0>(chainstates);
879 Chainstate& unvalidated_cs = *std::get<1>(chainstates);
887 return validation_chainstate.
CoinsTip());
900 res =
WITH_LOCK(
::cs_main,
return chainman.MaybeValidateSnapshot(validation_chainstate, unvalidated_cs));
908 BOOST_CHECK(!chainman.m_chainstates[0]->SnapshotBase());
910 BOOST_CHECK(chainman.m_chainstates[1]->SnapshotBase());
913 fs::path snapshot_invalid_dir =
gArgs.
GetDataDirNet() /
"chainstate_snapshot_INVALID";
921 BOOST_TEST_MESSAGE(
"Performing Load/Verify/Activate of chainstate");
925 this->LoadVerifyActivateChainstate();
938 "Ensure we can mine blocks on top of the \"new\" IBD chainstate");
947template <
typename Options>
949 const std::vector<const char*>&
args)
951 const auto argv{
Cat({
"ignore"},
args)};
964 auto get_opts = [&](
const std::vector<const char*>&
args) {
969 .notifications = notifications};
973 auto get_valid_opts = [&](
const std::vector<const char*>&
args) {
974 const auto result{get_opts(
args)};
980 BOOST_CHECK(!get_valid_opts({}).assumed_valid_block);
986 std::string assume_valid{
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"};
990 BOOST_CHECK(!get_opts({
"-assumevalid=01234567890123456789012345678901234567890123456789012345678901234"}));
993 BOOST_CHECK(!get_valid_opts({}).minimum_chain_work);
998 std::string minimum_chainwork{
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"};
1001 BOOST_CHECK(!get_opts({
"-minimumchainwork=xyz"}));
1002 BOOST_CHECK(!get_opts({
"-minimumchainwork=01234567890123456789012345678901234567890123456789012345678901234"}));
1005 BOOST_CHECK_EQUAL(get_valid_opts({
"-prevoutfetchthreads=0"}).prevoutfetch_threads_num, 0);
1006 BOOST_CHECK_EQUAL(get_valid_opts({
"-prevoutfetchthreads=3"}).prevoutfetch_threads_num, 3);
1008 BOOST_CHECK(!get_opts({
"-prevoutfetchthreads=-1"}));
arith_uint256 UintToArith256(const uint256 &a)
@ BLOCK_VALID_TREE
All parent headers found, difficulty matches, timestamp >= median previous.
@ BLOCK_FAILED_CHILD
Unused flag that was previously set when descending from failed block.
@ BLOCK_FAILED_VALID
stage after last reached validness failed
const CChainParams & Params()
Return the currently selected parameters.
static constexpr int32_t DEFAULT_PREVOUTFETCH_THREADS
#define Assert(val)
Identity function.
bool ParseParameters(int argc, const char *const argv[], std::string &error) EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
fs::path GetDataDirNet() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Get data directory path with appended network identifier.
Non-refcounted RAII wrapper for FILE*.
The block chain is a tree shaped structure starting with the genesis block at the root,...
CBlockIndex * pprev
pointer to the index of the predecessor of this block
uint64_t m_chain_tx_count
(memory only) Number of transactions in the chain up to and including this block.
arith_uint256 nChainWork
(memory only) Total amount of work (expected number of hashes) in the chain up to and including this ...
uint256 GetBlockHash() const
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
void SetTip(CBlockIndex &block)
Set/initialize a chain with a given tip.
int Height() const
Return the maximal height in the chain.
std::optional< AssumeutxoData > AssumeutxoForHeight(int height) const
CCoinsView that adds a memory cache for transactions to another CCoinsView.
void AddCoin(const COutPoint &outpoint, Coin &&coin, bool possible_overwrite)
Add a coin.
unsigned int GetCacheSize() const
Size of the cache (in number of transaction outputs)
bool HaveCoin(const COutPoint &outpoint) const override
Just check whether a given outpoint is unspent.
An outpoint - a combination of a transaction hash and an index n into its vout.
Serialized script, used inside transaction inputs and outputs.
Chainstate stores and provides an API to update our local knowledge of the current best chain.
CChain m_chain
The current chain of blockheaders we consult and build on.
size_t m_coinstip_cache_size_bytes
The cache size of the in-memory coins view.
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
size_t m_coinsdb_cache_size_bytes
The cache size of the on-disk coins view.
bool PreciousBlock(BlockValidationState &state, CBlockIndex *pindex) LOCKS_EXCLUDED(bool InvalidateBlock(BlockValidationState &state, CBlockIndex *pindex) LOCKS_EXCLUDED(void SetBlockFailureFlags(CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(voi ResetBlockFailureFlags)(CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Mark a block as precious and reorganize.
const std::optional< uint256 > m_from_snapshot_blockhash
The blockhash which is the base of the snapshot this chainstate was created from.
bool DisconnectTip(BlockValidationState &state, DisconnectedBlockTransactions *disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main
Disconnect m_chain's tip.
std::set< CBlockIndex *, node::CBlockIndexWorkComparator > setBlockIndexCandidates
The set of all CBlockIndex entries that have as much work as our current tip or more,...
RecursiveMutex * MempoolMutex() const LOCK_RETURNED(m_mempool -> cs)
Indirection necessary to make lock annotations work with an optional mempool.
Interface for managing multiple Chainstate objects, where each chainstate is associated with chainsta...
Chainstate * HistoricalChainstate() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Return historical chainstate targeting a specific block, if any.
size_t m_total_coinstip_cache
The total number of bytes available for us to use across all in-memory coins caches.
RecursiveMutex & GetMutex() const LOCK_RETURNED(
Alias for cs_main.
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Chainstate & ActiveChainstate() const
Alternatives to CurrentChainstate() used by older code to query latest chainstate information without...
SnapshotCompletionResult MaybeValidateSnapshot(Chainstate &validated_cs, Chainstate &unvalidated_cs) EXCLUSIVE_LOCKS_REQUIRED(Chainstate & CurrentChainstate() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Try to validate an assumeutxo snapshot by using a validated historical chainstate targeted at the sna...
size_t m_total_coinsdb_cache
The total number of bytes available for us to use across all leveldb coins databases.
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Load the block tree and coins database from disk, initializing state if we're running with -reindex.
CChain & ActiveChain() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
void ResetBlockSequenceCounters() EXCLUSIVE_LOCKS_REQUIRED(
node::BlockManager m_blockman
A single BlockManager instance is shared across each constructed chainstate to avoid duplicating bloc...
CTxOut out
unspent transaction output
uint32_t nHeight
at which height this containing transaction was included in the active block chain
DisconnectedBlockTransactions.
256-bit unsigned big integer.
A base class defining functions for notifying about certain kernel events.
Maintains a tree of blocks (stored in m_block_index) which is consulted to determine where the most-w...
void assign(size_type n, const T &val)
static transaction_identifier FromUint256(const uint256 &id)
static const uint256 ZERO
static std::optional< uint256 > FromHex(std::string_view str)
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
static const unsigned int MAX_DISCONNECTED_TX_POOL_BYTES
Maximum bytes for transactions to store for processing during reorg.
static bool exists(const path &p)
BOOST_CHECK_EQUAL(headers.FindFirst("key"), "value")
util::Result< void > ApplyArgsManOptions(const ArgsManager &args, BlockManager::Options &opts)
std::optional< fs::path > FindAssumeutxoChainstateDir(const fs::path &data_dir)
Return a path to the snapshot-based chainstate dir, if one exists.
std::optional< uint256 > ReadSnapshotBaseBlockhash(fs::path chaindir)
bilingual_str ErrorString(const Result< T > &result)
#define BOOST_CHECK(expr)
std::shared_ptr< const CTransaction > CTransactionRef
std::vector< unsigned char > ToByteVector(const T &in)
uint64_t ReadCompactSize(Stream &is, bool range_check=true)
Decode a CompactSize-encoded variable-length integer.
Testing setup that performs all steps up until right before ChainstateManager gets initialized.
Application-specific storage settings.
fs::path path
Location in the filesystem where leveldb data will be stored.
std::tuple< Chainstate *, Chainstate * > SetupSnapshot()
ChainstateManager & SimulateNodeRestart()
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Testing setup that configures a complete environment.
An options struct for ChainstateManager, more ergonomically referred to as ChainstateManager::Options...
const CChainParams & chainparams
std::unique_ptr< ValidationSignals > validation_signals
Issues calls about blocks and transactions.
std::unique_ptr< CTxMemPool > mempool
std::unique_ptr< ChainstateManager > chainman
std::unique_ptr< node::Warnings > warnings
Manages all the node warnings.
std::function< bool()> shutdown_request
Function to request a shutdown.
std::unique_ptr< KernelNotifications > notifications
Issues blocking calls about sync status, errors and warnings.
util::SignalInterrupt * shutdown_signal
Interrupt object used to track whether node shutdown was requested.
std::atomic< int > exit_status
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
static bool CreateAndActivateUTXOSnapshot(TestingSetup *fixture, F malleation=NoMalleation, bool reset_chainstate=false, bool in_memory_chainstate=false)
Create and activate a UTXO snapshot, optionally providing a function to malleate the snapshot.
#define ASSERT_DEBUG_LOG(message)
consteval auto _(util::TranslatedLiteral str)
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
@ VALIDATED
Every block in the chain has been validated.
@ UNVALIDATED
Blocks after an assumeutxo snapshot have been validated but the snapshot itself has not been validate...
@ INVALID
The assumeutxo snapshot failed validation.
static constexpr int32_t MAX_PREVOUTFETCH_THREADS
Maximum number of dedicated threads allowed for prefetching block input prevouts.
BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)
Basic tests for ChainstateManager.
util::Result< Options > SetOptsFromArgs(ArgsManager &args_man, Options opts, const std::vector< const char * > &args)
Helper function to parse args into args_man and return the result of applying them to opts.
V Cat(V v1, V &&v2)
Concatenate two vectors, moving elements.