mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-04 03:59:26 +01:00
fd06157d14test: Add coverage for restarted node without any block sync (Fabian Jahr)3d7ab7ecb7rpc, test: Address feedback from #29668 (Fabian Jahr)312919c9ddtest: Indices can not start based on block data without undo data (Fabian Jahr)a9a3b29dd6index: Check availability of undo data for indices (Fabian Jahr)881ab4fc82support multiple block status checks in CheckBlockDataAvailability (furszy) Pull request description: Currently, we check that `BLOCK_HAVE_DATA` is available for all blocks an index needs to sync during startup. However, for `coinstatsindex` and `blockfilterindex` we also need the undo data for these blocks. If that data is missing in the blocks, we are currently still starting to sync each of these indices and then crash later when we encounter the missing data. This PR adds explicit knowledge of which block data is needed for each index and then checks its availability during startup before initializing the sync process on them. This also addresses a few open comments from #29668 in the last commit. ACKs for top commit: achow101: ACKfd06157d14furszy: utACKfd06157d14sedited: Re-ACKfd06157d14Tree-SHA512: e2ed81c93372b02daa8ddf2819df4164f96d92de05b1d48855410ecac78d5fcd9612d7f0e63a9d57d7e75a0b46e1bea278e43ea87f2693af0220d1f9c600e416
151 lines
4.9 KiB
C++
151 lines
4.9 KiB
C++
// Copyright (c) 2017-present 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 <chain.h>
|
|
#include <node/blockstorage.h>
|
|
#include <rpc/blockchain.h>
|
|
#include <sync.h>
|
|
#include <test/util/setup_common.h>
|
|
#include <util/string.h>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <cstdlib>
|
|
|
|
using util::ToString;
|
|
|
|
/* Equality between doubles is imprecise. Comparison should be done
|
|
* with a small threshold of tolerance, rather than exact equality.
|
|
*/
|
|
static bool DoubleEquals(double a, double b, double epsilon)
|
|
{
|
|
return std::abs(a - b) < epsilon;
|
|
}
|
|
|
|
static CBlockIndex* CreateBlockIndexWithNbits(uint32_t nbits)
|
|
{
|
|
CBlockIndex* block_index = new CBlockIndex();
|
|
block_index->nHeight = 46367;
|
|
block_index->nTime = 1269211443;
|
|
block_index->nBits = nbits;
|
|
return block_index;
|
|
}
|
|
|
|
static void RejectDifficultyMismatch(double difficulty, double expected_difficulty) {
|
|
BOOST_CHECK_MESSAGE(
|
|
DoubleEquals(difficulty, expected_difficulty, 0.00001),
|
|
"Difficulty was " + ToString(difficulty)
|
|
+ " but was expected to be " + ToString(expected_difficulty));
|
|
}
|
|
|
|
/* Given a BlockIndex with the provided nbits,
|
|
* verify that the expected difficulty results.
|
|
*/
|
|
static void TestDifficulty(uint32_t nbits, double expected_difficulty)
|
|
{
|
|
CBlockIndex* block_index = CreateBlockIndexWithNbits(nbits);
|
|
double difficulty = GetDifficulty(*block_index);
|
|
delete block_index;
|
|
|
|
RejectDifficultyMismatch(difficulty, expected_difficulty);
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(blockchain_tests, BasicTestingSetup)
|
|
|
|
BOOST_AUTO_TEST_CASE(get_difficulty_for_very_low_target)
|
|
{
|
|
TestDifficulty(0x1f111111, 0.000001);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(get_difficulty_for_low_target)
|
|
{
|
|
TestDifficulty(0x1ef88f6f, 0.000016);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(get_difficulty_for_mid_target)
|
|
{
|
|
TestDifficulty(0x1df88f6f, 0.004023);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(get_difficulty_for_high_target)
|
|
{
|
|
TestDifficulty(0x1cf88f6f, 1.029916);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(get_difficulty_for_very_high_target)
|
|
{
|
|
TestDifficulty(0x12345678, 5913134931067755359633408.0);
|
|
}
|
|
|
|
//! Prune chain from height down to genesis block and check that
|
|
//! GetPruneHeight returns the correct value
|
|
static void CheckGetPruneHeight(const node::BlockManager& blockman, const CChain& chain, int height) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
|
{
|
|
AssertLockHeld(::cs_main);
|
|
|
|
// Emulate pruning all blocks from `height` down to the genesis block
|
|
// by unsetting the `BLOCK_HAVE_DATA` flag from `nStatus`
|
|
for (CBlockIndex* it{chain[height]}; it != nullptr && it->nHeight > 0; it = it->pprev) {
|
|
it->nStatus &= ~BLOCK_HAVE_DATA;
|
|
}
|
|
|
|
const auto prune_height{GetPruneHeight(blockman, chain)};
|
|
BOOST_REQUIRE(prune_height.has_value());
|
|
BOOST_CHECK_EQUAL(*prune_height, height);
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_CASE(get_prune_height, TestChain100Setup)
|
|
{
|
|
LOCK(::cs_main);
|
|
const auto& chain = m_node.chainman->ActiveChain();
|
|
const auto& blockman = m_node.chainman->m_blockman;
|
|
|
|
// Fresh chain of 100 blocks without any pruned blocks, so std::nullopt should be returned
|
|
BOOST_CHECK(!GetPruneHeight(blockman, chain).has_value());
|
|
|
|
// Start pruning
|
|
CheckGetPruneHeight(blockman, chain, 1);
|
|
CheckGetPruneHeight(blockman, chain, 99);
|
|
CheckGetPruneHeight(blockman, chain, 100);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(num_chain_tx_max)
|
|
{
|
|
CBlockIndex block_index{};
|
|
block_index.m_chain_tx_count = std::numeric_limits<uint64_t>::max();
|
|
BOOST_CHECK_EQUAL(block_index.m_chain_tx_count, std::numeric_limits<uint64_t>::max());
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_CASE(invalidate_block, TestChain100Setup)
|
|
{
|
|
const CChain& active{*WITH_LOCK(Assert(m_node.chainman)->GetMutex(), return &Assert(m_node.chainman)->ActiveChain())};
|
|
|
|
// Check BlockStatus when doing InvalidateBlock()
|
|
BlockValidationState state;
|
|
auto* orig_tip = active.Tip();
|
|
int height_to_invalidate = orig_tip->nHeight - 10;
|
|
auto* tip_to_invalidate = active[height_to_invalidate];
|
|
m_node.chainman->ActiveChainstate().InvalidateBlock(state, tip_to_invalidate);
|
|
|
|
// tip_to_invalidate just got invalidated, so it's BLOCK_FAILED_VALID
|
|
WITH_LOCK(::cs_main, assert(tip_to_invalidate->nStatus & BLOCK_FAILED_VALID));
|
|
|
|
// check all ancestors of the invalidated block are validated up to BLOCK_VALID_TRANSACTIONS and are not invalid
|
|
auto pindex = tip_to_invalidate->pprev;
|
|
while (pindex) {
|
|
WITH_LOCK(::cs_main, assert(pindex->IsValid(BLOCK_VALID_TRANSACTIONS)));
|
|
WITH_LOCK(::cs_main, assert((pindex->nStatus & BLOCK_FAILED_VALID) == 0));
|
|
pindex = pindex->pprev;
|
|
}
|
|
|
|
// check all descendants of the invalidated block are BLOCK_FAILED_VALID
|
|
pindex = orig_tip;
|
|
while (pindex && pindex != tip_to_invalidate) {
|
|
WITH_LOCK(::cs_main, assert(pindex->nStatus & BLOCK_FAILED_VALID));
|
|
pindex = pindex->pprev;
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|