2020-04-16 13:14:08 -04:00
|
|
|
// Copyright (c) 2019-2020 The Bitcoin Core developers
|
2019-12-12 16:11:03 -03:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
//
|
|
|
|
#include <chainparams.h>
|
|
|
|
#include <consensus/validation.h>
|
2020-08-25 13:52:51 -04:00
|
|
|
#include <node/utxo_snapshot.h>
|
2020-04-12 20:22:38 -04:00
|
|
|
#include <random.h>
|
2020-08-25 13:52:51 -04:00
|
|
|
#include <rpc/blockchain.h>
|
2019-12-12 16:11:03 -03:00
|
|
|
#include <sync.h>
|
|
|
|
#include <test/util/setup_common.h>
|
2020-04-12 20:22:38 -04:00
|
|
|
#include <uint256.h>
|
2019-12-12 16:11:03 -03:00
|
|
|
#include <validation.h>
|
2020-04-12 20:22:38 -04:00
|
|
|
#include <validationinterface.h>
|
2019-12-12 16:11:03 -03:00
|
|
|
|
2020-08-25 13:52:51 -04:00
|
|
|
#include <tinyformat.h>
|
|
|
|
#include <univalue.h>
|
|
|
|
|
2019-12-12 16:11:03 -03:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
|
test: Add new ChainTestingSetup and use it
Previously, the validation_chainstatemanager_tests test suite
instantiated its own duplicate ChainstateManager on which tests were
performed.
This wasn't a problem for the specific actions performed in
that suite. However, the existence of this duplicate ChainstateManager
and the fact that many of our validation static functions reach for
g_chainman, ::Chain(state|)Active means we may end up acting on two
different CChainStates should we write more extensive tests in the
future.
This change adds a new ChainTestingSetup which performs all
initialization previously done by TestingSetup except:
1. RPC command registration
2. ChainState initialization
3. Genesis Activation
4. {Ban,Conn,Peer}Man initialization
Means that we will no longer need to initialize a duplicate
ChainstateManger in order to test the initialization codepaths of
CChainState and ChainstateManager.
Lastly, this change has the additional benefit of allowing for
review-only assertions meant to show correctness to work in future work
de-globalizing g_chainman.
In the test chainstatemanager_rebalance_caches, an additional
LoadGenesisBlock call is added as MaybeReblanaceCaches eventually calls
FlushBlockFile, which tries to access vinfoBlockFile[nLastBlockFile],
which is out of bounds when LoadGenesisBlock hasn't been called yet.
-----
Note for the future:
The class con/destructor inheritance structure we have for these
TestingSetup classes is probably not the most suitable abstraction. In
particular, for both TestingSetup and ChainTestingSetup, we need to stop
the scheduler first before anything else. Otherwise classes depending on
the scheduler may be referenced by the scheduler after said classes are
freed. This means that there's no clear parallel between our teardown
code and C++'s destructuring order for class hierarchies.
Future work should strive to coalesce (as much as possible) test and
non-test init codepaths and perhaps structure it in a more fail-proof
way.
2020-10-13 17:55:20 -03:00
|
|
|
BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, ChainTestingSetup)
|
2019-12-12 16:11:03 -03:00
|
|
|
|
|
|
|
//! Basic tests for ChainstateManager.
|
|
|
|
//!
|
|
|
|
//! First create a legacy (IBD) chainstate, then create a snapshot chainstate.
|
|
|
|
BOOST_AUTO_TEST_CASE(chainstatemanager)
|
|
|
|
{
|
test: Add new ChainTestingSetup and use it
Previously, the validation_chainstatemanager_tests test suite
instantiated its own duplicate ChainstateManager on which tests were
performed.
This wasn't a problem for the specific actions performed in
that suite. However, the existence of this duplicate ChainstateManager
and the fact that many of our validation static functions reach for
g_chainman, ::Chain(state|)Active means we may end up acting on two
different CChainStates should we write more extensive tests in the
future.
This change adds a new ChainTestingSetup which performs all
initialization previously done by TestingSetup except:
1. RPC command registration
2. ChainState initialization
3. Genesis Activation
4. {Ban,Conn,Peer}Man initialization
Means that we will no longer need to initialize a duplicate
ChainstateManger in order to test the initialization codepaths of
CChainState and ChainstateManager.
Lastly, this change has the additional benefit of allowing for
review-only assertions meant to show correctness to work in future work
de-globalizing g_chainman.
In the test chainstatemanager_rebalance_caches, an additional
LoadGenesisBlock call is added as MaybeReblanaceCaches eventually calls
FlushBlockFile, which tries to access vinfoBlockFile[nLastBlockFile],
which is out of bounds when LoadGenesisBlock hasn't been called yet.
-----
Note for the future:
The class con/destructor inheritance structure we have for these
TestingSetup classes is probably not the most suitable abstraction. In
particular, for both TestingSetup and ChainTestingSetup, we need to stop
the scheduler first before anything else. Otherwise classes depending on
the scheduler may be referenced by the scheduler after said classes are
freed. This means that there's no clear parallel between our teardown
code and C++'s destructuring order for class hierarchies.
Future work should strive to coalesce (as much as possible) test and
non-test init codepaths and perhaps structure it in a more fail-proof
way.
2020-10-13 17:55:20 -03:00
|
|
|
ChainstateManager& manager = *m_node.chainman;
|
|
|
|
CTxMemPool& mempool = *m_node.mempool;
|
|
|
|
|
2019-12-12 16:11:03 -03:00
|
|
|
std::vector<CChainState*> chainstates;
|
|
|
|
const CChainParams& chainparams = Params();
|
|
|
|
|
2020-08-25 13:50:23 -04:00
|
|
|
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
|
|
|
|
|
2019-12-12 16:11:03 -03:00
|
|
|
// Create a legacy (IBD) chainstate.
|
|
|
|
//
|
2020-11-25 13:05:23 -03:00
|
|
|
CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(mempool));
|
2019-12-12 16:11:03 -03:00
|
|
|
chainstates.push_back(&c1);
|
|
|
|
c1.InitCoinsDB(
|
|
|
|
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
2019-09-16 16:01:12 -03:00
|
|
|
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
|
2019-12-12 16:11:03 -03:00
|
|
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
auto& active_chain = manager.ActiveChain();
|
|
|
|
BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain);
|
|
|
|
|
|
|
|
BOOST_CHECK_EQUAL(manager.ActiveHeight(), -1);
|
|
|
|
|
|
|
|
auto active_tip = manager.ActiveTip();
|
|
|
|
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);
|
|
|
|
|
2020-08-25 13:50:23 -04:00
|
|
|
BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
|
|
|
|
|
2019-12-12 16:11:03 -03:00
|
|
|
// Create a snapshot-based chainstate.
|
|
|
|
//
|
2020-08-25 13:50:23 -04:00
|
|
|
const uint256 snapshot_blockhash = GetRandHash();
|
|
|
|
CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
|
|
|
|
mempool, snapshot_blockhash));
|
2019-12-12 16:11:03 -03:00
|
|
|
chainstates.push_back(&c2);
|
2020-08-25 13:50:23 -04:00
|
|
|
|
|
|
|
BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
|
|
|
|
|
2019-12-12 16:11:03 -03:00
|
|
|
c2.InitCoinsDB(
|
|
|
|
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
2019-09-16 16:01:12 -03:00
|
|
|
WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23));
|
2019-12-12 16:11:03 -03:00
|
|
|
// Unlike c1, which doesn't have any blocks. Gets us different tip, height.
|
|
|
|
c2.LoadGenesisBlock(chainparams);
|
|
|
|
BlockValidationState _;
|
|
|
|
BOOST_CHECK(c2.ActivateBestChain(_, chainparams, nullptr));
|
|
|
|
|
|
|
|
BOOST_CHECK(manager.IsSnapshotActive());
|
|
|
|
BOOST_CHECK(!manager.IsSnapshotValidated());
|
|
|
|
BOOST_CHECK(manager.IsBackgroundIBD(&c1));
|
|
|
|
BOOST_CHECK(!manager.IsBackgroundIBD(&c2));
|
|
|
|
auto all2 = manager.GetAll();
|
|
|
|
BOOST_CHECK_EQUAL_COLLECTIONS(all2.begin(), all2.end(), chainstates.begin(), chainstates.end());
|
|
|
|
|
|
|
|
auto& active_chain2 = manager.ActiveChain();
|
|
|
|
BOOST_CHECK_EQUAL(&active_chain2, &c2.m_chain);
|
|
|
|
|
|
|
|
BOOST_CHECK_EQUAL(manager.ActiveHeight(), 0);
|
|
|
|
|
|
|
|
auto active_tip2 = manager.ActiveTip();
|
|
|
|
auto exp_tip2 = c2.m_chain.Tip();
|
|
|
|
BOOST_CHECK_EQUAL(active_tip2, exp_tip2);
|
|
|
|
|
|
|
|
// Ensure that these pointers actually correspond to different
|
|
|
|
// 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);
|
|
|
|
|
2020-04-12 20:22:38 -04:00
|
|
|
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded
|
|
|
|
SyncWithValidationInterfaceQueue();
|
|
|
|
|
2019-12-12 16:11:03 -03:00
|
|
|
WITH_LOCK(::cs_main, manager.Unload());
|
|
|
|
}
|
|
|
|
|
2019-09-16 14:37:29 -03:00
|
|
|
//! Test rebalancing the caches associated with each chainstate.
|
|
|
|
BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
|
|
|
|
{
|
test: Add new ChainTestingSetup and use it
Previously, the validation_chainstatemanager_tests test suite
instantiated its own duplicate ChainstateManager on which tests were
performed.
This wasn't a problem for the specific actions performed in
that suite. However, the existence of this duplicate ChainstateManager
and the fact that many of our validation static functions reach for
g_chainman, ::Chain(state|)Active means we may end up acting on two
different CChainStates should we write more extensive tests in the
future.
This change adds a new ChainTestingSetup which performs all
initialization previously done by TestingSetup except:
1. RPC command registration
2. ChainState initialization
3. Genesis Activation
4. {Ban,Conn,Peer}Man initialization
Means that we will no longer need to initialize a duplicate
ChainstateManger in order to test the initialization codepaths of
CChainState and ChainstateManager.
Lastly, this change has the additional benefit of allowing for
review-only assertions meant to show correctness to work in future work
de-globalizing g_chainman.
In the test chainstatemanager_rebalance_caches, an additional
LoadGenesisBlock call is added as MaybeReblanaceCaches eventually calls
FlushBlockFile, which tries to access vinfoBlockFile[nLastBlockFile],
which is out of bounds when LoadGenesisBlock hasn't been called yet.
-----
Note for the future:
The class con/destructor inheritance structure we have for these
TestingSetup classes is probably not the most suitable abstraction. In
particular, for both TestingSetup and ChainTestingSetup, we need to stop
the scheduler first before anything else. Otherwise classes depending on
the scheduler may be referenced by the scheduler after said classes are
freed. This means that there's no clear parallel between our teardown
code and C++'s destructuring order for class hierarchies.
Future work should strive to coalesce (as much as possible) test and
non-test init codepaths and perhaps structure it in a more fail-proof
way.
2020-10-13 17:55:20 -03:00
|
|
|
ChainstateManager& manager = *m_node.chainman;
|
|
|
|
CTxMemPool& mempool = *m_node.mempool;
|
|
|
|
|
2019-09-16 14:37:29 -03:00
|
|
|
size_t max_cache = 10000;
|
|
|
|
manager.m_total_coinsdb_cache = max_cache;
|
|
|
|
manager.m_total_coinstip_cache = max_cache;
|
|
|
|
|
|
|
|
std::vector<CChainState*> chainstates;
|
|
|
|
|
|
|
|
// Create a legacy (IBD) chainstate.
|
|
|
|
//
|
2020-11-25 13:05:23 -03:00
|
|
|
CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool));
|
2019-09-16 14:37:29 -03:00
|
|
|
chainstates.push_back(&c1);
|
|
|
|
c1.InitCoinsDB(
|
|
|
|
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
|
|
|
|
|
|
|
{
|
|
|
|
LOCK(::cs_main);
|
|
|
|
c1.InitCoinsCache(1 << 23);
|
test: Add new ChainTestingSetup and use it
Previously, the validation_chainstatemanager_tests test suite
instantiated its own duplicate ChainstateManager on which tests were
performed.
This wasn't a problem for the specific actions performed in
that suite. However, the existence of this duplicate ChainstateManager
and the fact that many of our validation static functions reach for
g_chainman, ::Chain(state|)Active means we may end up acting on two
different CChainStates should we write more extensive tests in the
future.
This change adds a new ChainTestingSetup which performs all
initialization previously done by TestingSetup except:
1. RPC command registration
2. ChainState initialization
3. Genesis Activation
4. {Ban,Conn,Peer}Man initialization
Means that we will no longer need to initialize a duplicate
ChainstateManger in order to test the initialization codepaths of
CChainState and ChainstateManager.
Lastly, this change has the additional benefit of allowing for
review-only assertions meant to show correctness to work in future work
de-globalizing g_chainman.
In the test chainstatemanager_rebalance_caches, an additional
LoadGenesisBlock call is added as MaybeReblanaceCaches eventually calls
FlushBlockFile, which tries to access vinfoBlockFile[nLastBlockFile],
which is out of bounds when LoadGenesisBlock hasn't been called yet.
-----
Note for the future:
The class con/destructor inheritance structure we have for these
TestingSetup classes is probably not the most suitable abstraction. In
particular, for both TestingSetup and ChainTestingSetup, we need to stop
the scheduler first before anything else. Otherwise classes depending on
the scheduler may be referenced by the scheduler after said classes are
freed. This means that there's no clear parallel between our teardown
code and C++'s destructuring order for class hierarchies.
Future work should strive to coalesce (as much as possible) test and
non-test init codepaths and perhaps structure it in a more fail-proof
way.
2020-10-13 17:55:20 -03:00
|
|
|
BOOST_REQUIRE(c1.LoadGenesisBlock(Params()));
|
2019-09-16 14:37:29 -03:00
|
|
|
c1.CoinsTip().SetBestBlock(InsecureRand256());
|
|
|
|
manager.MaybeRebalanceCaches();
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_CHECK_EQUAL(c1.m_coinstip_cache_size_bytes, max_cache);
|
|
|
|
BOOST_CHECK_EQUAL(c1.m_coinsdb_cache_size_bytes, max_cache);
|
|
|
|
|
|
|
|
// Create a snapshot-based chainstate.
|
|
|
|
//
|
2020-11-25 13:05:23 -03:00
|
|
|
CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(mempool, GetRandHash()));
|
2019-09-16 14:37:29 -03:00
|
|
|
chainstates.push_back(&c2);
|
|
|
|
c2.InitCoinsDB(
|
|
|
|
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
|
|
|
|
|
|
|
|
{
|
|
|
|
LOCK(::cs_main);
|
|
|
|
c2.InitCoinsCache(1 << 23);
|
test: Add new ChainTestingSetup and use it
Previously, the validation_chainstatemanager_tests test suite
instantiated its own duplicate ChainstateManager on which tests were
performed.
This wasn't a problem for the specific actions performed in
that suite. However, the existence of this duplicate ChainstateManager
and the fact that many of our validation static functions reach for
g_chainman, ::Chain(state|)Active means we may end up acting on two
different CChainStates should we write more extensive tests in the
future.
This change adds a new ChainTestingSetup which performs all
initialization previously done by TestingSetup except:
1. RPC command registration
2. ChainState initialization
3. Genesis Activation
4. {Ban,Conn,Peer}Man initialization
Means that we will no longer need to initialize a duplicate
ChainstateManger in order to test the initialization codepaths of
CChainState and ChainstateManager.
Lastly, this change has the additional benefit of allowing for
review-only assertions meant to show correctness to work in future work
de-globalizing g_chainman.
In the test chainstatemanager_rebalance_caches, an additional
LoadGenesisBlock call is added as MaybeReblanaceCaches eventually calls
FlushBlockFile, which tries to access vinfoBlockFile[nLastBlockFile],
which is out of bounds when LoadGenesisBlock hasn't been called yet.
-----
Note for the future:
The class con/destructor inheritance structure we have for these
TestingSetup classes is probably not the most suitable abstraction. In
particular, for both TestingSetup and ChainTestingSetup, we need to stop
the scheduler first before anything else. Otherwise classes depending on
the scheduler may be referenced by the scheduler after said classes are
freed. This means that there's no clear parallel between our teardown
code and C++'s destructuring order for class hierarchies.
Future work should strive to coalesce (as much as possible) test and
non-test init codepaths and perhaps structure it in a more fail-proof
way.
2020-10-13 17:55:20 -03:00
|
|
|
BOOST_REQUIRE(c2.LoadGenesisBlock(Params()));
|
2019-09-16 14:37:29 -03:00
|
|
|
c2.CoinsTip().SetBestBlock(InsecureRand256());
|
|
|
|
manager.MaybeRebalanceCaches();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since both chainstates are considered to be in initial block download,
|
|
|
|
// the snapshot chainstate should take priority.
|
|
|
|
BOOST_CHECK_CLOSE(c1.m_coinstip_cache_size_bytes, max_cache * 0.05, 1);
|
|
|
|
BOOST_CHECK_CLOSE(c1.m_coinsdb_cache_size_bytes, max_cache * 0.05, 1);
|
|
|
|
BOOST_CHECK_CLOSE(c2.m_coinstip_cache_size_bytes, max_cache * 0.95, 1);
|
|
|
|
BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
|
|
|
|
}
|
|
|
|
|
2020-11-19 11:30:13 -03:00
|
|
|
auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
|
|
|
|
|
|
|
|
template<typename F = decltype(NoMalleation)>
|
2020-08-25 13:52:51 -04:00
|
|
|
static bool
|
2020-11-19 11:30:13 -03:00
|
|
|
CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
|
2020-08-25 13:52:51 -04:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
|
2020-11-19 11:30:13 -03:00
|
|
|
malleation(auto_infile, metadata);
|
|
|
|
|
2020-08-25 13:52:51 -04:00
|
|
|
return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Test basic snapshot activation.
|
|
|
|
BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100DeterministicSetup)
|
|
|
|
{
|
|
|
|
ChainstateManager& chainman = *Assert(m_node.chainman);
|
|
|
|
|
|
|
|
size_t initial_size;
|
|
|
|
size_t initial_total_coins{100};
|
|
|
|
|
|
|
|
// Make some initial assertions about the contents of the chainstate.
|
|
|
|
{
|
|
|
|
LOCK(::cs_main);
|
|
|
|
CCoinsViewCache& ibd_coinscache = chainman.ActiveChainstate().CoinsTip();
|
|
|
|
initial_size = ibd_coinscache.GetCacheSize();
|
|
|
|
size_t total_coins{0};
|
|
|
|
|
|
|
|
for (CTransactionRef& txn : m_coinbase_txns) {
|
|
|
|
COutPoint op{txn->GetHash(), 0};
|
|
|
|
BOOST_CHECK(ibd_coinscache.HaveCoin(op));
|
|
|
|
total_coins++;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
|
|
|
|
BOOST_CHECK_EQUAL(initial_size, initial_total_coins);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Snapshot should refuse to load at this height.
|
|
|
|
BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
|
|
|
|
BOOST_CHECK(chainman.ActiveChainstate().m_from_snapshot_blockhash.IsNull());
|
|
|
|
BOOST_CHECK_EQUAL(
|
|
|
|
chainman.ActiveChainstate().m_from_snapshot_blockhash,
|
|
|
|
chainman.SnapshotBlockhash().value_or(uint256()));
|
|
|
|
|
|
|
|
// Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
|
|
|
|
// be found.
|
|
|
|
mineBlocks(10);
|
|
|
|
initial_size += 10;
|
|
|
|
initial_total_coins += 10;
|
|
|
|
|
2020-11-19 11:30:13 -03:00
|
|
|
// Should not load malleated snapshots
|
|
|
|
BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
|
|
|
|
m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
|
|
|
|
// A UTXO is missing but count is correct
|
|
|
|
metadata.m_coins_count -= 1;
|
|
|
|
|
|
|
|
COutPoint outpoint;
|
|
|
|
Coin coin;
|
|
|
|
|
|
|
|
auto_infile >> outpoint;
|
|
|
|
auto_infile >> coin;
|
|
|
|
}));
|
|
|
|
BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
|
|
|
|
m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
|
|
|
|
// Coins count is larger than coins in file
|
|
|
|
metadata.m_coins_count += 1;
|
|
|
|
}));
|
|
|
|
BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
|
|
|
|
m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
|
|
|
|
// Coins count is smaller than coins in file
|
|
|
|
metadata.m_coins_count -= 1;
|
|
|
|
}));
|
|
|
|
|
2020-08-25 13:52:51 -04:00
|
|
|
BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
|
|
|
|
|
|
|
|
// Ensure our active chain is the snapshot chainstate.
|
|
|
|
BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash.IsNull());
|
|
|
|
BOOST_CHECK_EQUAL(
|
|
|
|
chainman.ActiveChainstate().m_from_snapshot_blockhash,
|
|
|
|
*chainman.SnapshotBlockhash());
|
|
|
|
|
|
|
|
// To be checked against later when we try loading a subsequent snapshot.
|
|
|
|
uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
|
|
|
|
|
|
|
|
// Make some assertions about the both chainstates. These checks ensure the
|
|
|
|
// legacy chainstate hasn't changed and that the newly created chainstate
|
|
|
|
// reflects the expected content.
|
|
|
|
{
|
|
|
|
LOCK(::cs_main);
|
|
|
|
int chains_tested{0};
|
|
|
|
|
|
|
|
for (CChainState* chainstate : chainman.GetAll()) {
|
|
|
|
BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
|
|
|
|
CCoinsViewCache& coinscache = chainstate->CoinsTip();
|
|
|
|
|
|
|
|
// Both caches will be empty initially.
|
|
|
|
BOOST_CHECK_EQUAL((unsigned int)0, coinscache.GetCacheSize());
|
|
|
|
|
|
|
|
size_t total_coins{0};
|
|
|
|
|
|
|
|
for (CTransactionRef& txn : m_coinbase_txns) {
|
|
|
|
COutPoint op{txn->GetHash(), 0};
|
|
|
|
BOOST_CHECK(coinscache.HaveCoin(op));
|
|
|
|
total_coins++;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_CHECK_EQUAL(initial_size , coinscache.GetCacheSize());
|
|
|
|
BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
|
|
|
|
chains_tested++;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_CHECK_EQUAL(chains_tested, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mine some new blocks on top of the activated snapshot chainstate.
|
|
|
|
constexpr size_t new_coins{100};
|
|
|
|
mineBlocks(new_coins); // Defined in TestChain100Setup.
|
|
|
|
|
|
|
|
{
|
|
|
|
LOCK(::cs_main);
|
|
|
|
size_t coins_in_active{0};
|
|
|
|
size_t coins_in_ibd{0};
|
|
|
|
size_t coins_missing_ibd{0};
|
|
|
|
|
|
|
|
for (CChainState* chainstate : chainman.GetAll()) {
|
|
|
|
BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
|
|
|
|
CCoinsViewCache& coinscache = chainstate->CoinsTip();
|
|
|
|
bool is_ibd = chainman.IsBackgroundIBD(chainstate);
|
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Snapshot should refuse to load after one has already loaded.
|
|
|
|
BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
|
|
|
|
|
|
|
|
// Snapshot blockhash should be unchanged.
|
|
|
|
BOOST_CHECK_EQUAL(
|
|
|
|
chainman.ActiveChainstate().m_from_snapshot_blockhash,
|
|
|
|
loaded_snapshot_blockhash);
|
|
|
|
}
|
|
|
|
|
2019-12-12 16:11:03 -03:00
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|