#include <chain.h>
#include <chainparams.h>
#include <clientversion.h>
#include <node/blockstorage.h>
#include <node/context.h>
#include <node/kernel_notifications.h>
#include <script/solver.h>
#include <primitives/block.h>
#include <util/chaintype.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
#include <test/util/logging.h>
#include <test/util/setup_common.h>
using node::STORAGE_HEADER_BYTES;
using node::BlockManager;
using node::KernelNotifications;
using node::MAX_BLOCKFILE_SIZE;
BOOST_FIXTURE_TEST_SUITE(blockmanager_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos)
{
const auto params {CreateChainParams(ArgsManager{}, ChainType::MAIN)};
KernelNotifications notifications{Assert(m_node.shutdown_request), m_node.exit_status, *Assert(m_node.warnings)};
const BlockManager::Options blockman_opts{
.chainparams = *params,
.blocks_dir = m_args.GetBlocksDirPath(),
.notifications = notifications,
.block_tree_db_params = DBParams{
.path = m_args.GetDataDirNet() / "blocks" / "index",
.cache_bytes = 0,
},
};
BlockManager blockman{*Assert(m_node.shutdown_signal), blockman_opts};
BOOST_CHECK_EQUAL(blockman.WriteBlock(params->GenesisBlock(), 0).nPos, STORAGE_HEADER_BYTES);
const FlatFilePos pos{0, STORAGE_HEADER_BYTES};
blockman.UpdateBlockInfo(params->GenesisBlock(), 0, pos);
FlatFilePos actual{blockman.WriteBlock(params->GenesisBlock(), 1)};
BOOST_CHECK_EQUAL(actual.nPos, STORAGE_HEADER_BYTES + ::GetSerializeSize(TX_WITH_WITNESS(params->GenesisBlock())) + STORAGE_HEADER_BYTES);
}
BOOST_FIXTURE_TEST_CASE(blockmanager_scan_unlink_already_pruned_files, TestChain100Setup)
{
const auto& chainman = Assert(m_node.chainman);
auto& blockman = chainman->m_blockman;
const CBlockIndex* old_tip{WITH_LOCK(chainman->GetMutex(), return chainman->ActiveChain().Tip())};
WITH_LOCK(chainman->GetMutex(), blockman.GetBlockFileInfo(old_tip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE);
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
int file_number;
{
LOCK(chainman->GetMutex());
file_number = old_tip->GetBlockPos().nFile;
blockman.PruneOneBlockFile(file_number);
}
const FlatFilePos pos(file_number, 0);
WITH_LOCK(chainman->GetMutex(), blockman.ScanAndUnlinkAlreadyPrunedFiles());
BOOST_CHECK(!blockman.OpenBlockFile(pos, true).IsNull());
blockman.m_have_pruned = true;
WITH_LOCK(chainman->GetMutex(), blockman.ScanAndUnlinkAlreadyPrunedFiles());
BOOST_CHECK(blockman.OpenBlockFile(pos, true).IsNull());
WITH_LOCK(chainman->GetMutex(), blockman.ScanAndUnlinkAlreadyPrunedFiles());
const CBlockIndex* new_tip{WITH_LOCK(chainman->GetMutex(), return chainman->ActiveChain().Tip())};
BOOST_CHECK_NE(old_tip, new_tip);
const int new_file_number{WITH_LOCK(chainman->GetMutex(), return new_tip->GetBlockPos().nFile)};
const FlatFilePos new_pos(new_file_number, 0);
BOOST_CHECK(!blockman.OpenBlockFile(new_pos, true).IsNull());
}
BOOST_FIXTURE_TEST_CASE(blockmanager_block_data_availability, TestChain100Setup)
{
LOCK(::cs_main);
auto& chainman = m_node.chainman;
auto& blockman = chainman->m_blockman;
const CBlockIndex& tip = *chainman->ActiveTip();
const auto& func_prune_blocks = [&](CBlockIndex* last_pruned_block)
{
LOCK(::cs_main);
CBlockIndex* it = last_pruned_block;
while (it != nullptr && it->nStatus & BLOCK_HAVE_DATA) {
it->nStatus &= ~BLOCK_HAVE_DATA;
it = it->pprev;
}
};
BOOST_CHECK_EQUAL(blockman.GetFirstBlock(tip, BLOCK_HAVE_DATA), chainman->ActiveChain()[0]);
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *chainman->ActiveChain()[0]));
CBlockIndex* lower_block = chainman->ActiveChain()[tip.nHeight / 2];
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *lower_block));
int height_to_prune = tip.nHeight / 2;
CBlockIndex* first_available_block = chainman->ActiveChain()[height_to_prune + 1];
CBlockIndex* last_pruned_block = first_available_block->pprev;
func_prune_blocks(last_pruned_block);
BOOST_CHECK_EQUAL(blockman.GetFirstBlock(tip, BLOCK_HAVE_DATA), first_available_block);
BOOST_CHECK(blockman.CheckBlockDataAvailability(tip, *first_available_block));
BOOST_CHECK(!blockman.CheckBlockDataAvailability(tip, *last_pruned_block));
}
BOOST_FIXTURE_TEST_CASE(blockmanager_readblock_hash_mismatch, TestingSetup)
{
CBlockIndex* fake_index{WITH_LOCK(m_node.chainman->GetMutex(), return m_node.chainman->ActiveChain().Tip())};
fake_index->phashBlock = &uint256::ONE;
ASSERT_DEBUG_LOG("GetHash() doesn't match index");
CBlock dummy;
BOOST_CHECK(!m_node.chainman->m_blockman.ReadBlock(dummy, *fake_index));
}
BOOST_AUTO_TEST_CASE(blockmanager_flush_block_file)
{
KernelNotifications notifications{Assert(m_node.shutdown_request), m_node.exit_status, *Assert(m_node.warnings)};
node::BlockManager::Options blockman_opts{
.chainparams = Params(),
.blocks_dir = m_args.GetBlocksDirPath(),
.notifications = notifications,
.block_tree_db_params = DBParams{
.path = m_args.GetDataDirNet() / "blocks" / "index",
.cache_bytes = 0,
},
};
BlockManager blockman{*Assert(m_node.shutdown_signal), blockman_opts};
CBlock block1;
block1.nVersion = 1;
CBlock block2;
block2.nVersion = 2;
CBlock block3;
block3.nVersion = 3;
constexpr int TEST_BLOCK_SIZE{81};
BOOST_CHECK_EQUAL(blockman.CalculateCurrentUsage(), 0);
FlatFilePos pos1{blockman.WriteBlock(block1, 1)};
FlatFilePos pos2{blockman.WriteBlock(block2, 2)};
BOOST_CHECK_EQUAL(blockman.CalculateCurrentUsage(), (TEST_BLOCK_SIZE + STORAGE_HEADER_BYTES) * 2);
CBlock read_block;
BOOST_CHECK_EQUAL(read_block.nVersion, 0);
{
ASSERT_DEBUG_LOG("Errors in block header");
BOOST_CHECK(!blockman.ReadBlock(read_block, pos1, {}));
BOOST_CHECK_EQUAL(read_block.nVersion, 1);
}
{
ASSERT_DEBUG_LOG("Errors in block header");
BOOST_CHECK(!blockman.ReadBlock(read_block, pos2, {}));
BOOST_CHECK_EQUAL(read_block.nVersion, 2);
}
CBlockFileInfo* block_data = blockman.GetBlockFileInfo(0);
BOOST_CHECK_EQUAL(block_data->nBlocks, 2);
blockman.UpdateBlockInfo(block3, 3, pos2);
BOOST_CHECK_EQUAL(block_data->nBlocks, 3);
BOOST_CHECK_EQUAL(blockman.CalculateCurrentUsage(), (TEST_BLOCK_SIZE + STORAGE_HEADER_BYTES) * 2);
BOOST_CHECK(!blockman.ReadBlock(read_block, pos2, {}));
BOOST_CHECK_EQUAL(read_block.nVersion, 2);
}
BOOST_AUTO_TEST_SUITE_END()