mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-08 14:47:31 +02:00
kernel: Add block validation to C header
The added function allows the user process and validate a given block with the chainstate manager. The *_process_block(...) function does some preliminary checks on the block before passing it to `ProcessNewBlock(...)`. These are similar to the checks in the `submitblock()` rpc. Richer processing of the block validation result will be made available in the following commits through the validation interface. The commits also adds a utility for deserializing a `CBlock` (`kernel_block_create()`) that may then be passed to the library for processing. The tests exercise the function for both mainnet and regtest. The commit also adds the data of 206 regtest blocks (some blocks also contain transactions).
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
#define BOOST_TEST_MODULE Bitcoin Kernel Test Suite
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <test/kernel/block_data.h>
|
||||
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
@@ -470,6 +472,15 @@ BOOST_AUTO_TEST_CASE(btck_context_tests)
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(btck_block)
|
||||
{
|
||||
Block block{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[0])};
|
||||
Block block_1{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[1])};
|
||||
CheckHandle(block, block_1);
|
||||
Block block_tx{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[205])};
|
||||
CheckRange(block_tx.Transactions(), block_tx.CountTransactions());
|
||||
}
|
||||
|
||||
Context create_context(std::shared_ptr<TestKernelNotifications> notifications, ChainType chain_type)
|
||||
{
|
||||
ContextOptions options{};
|
||||
@@ -505,3 +516,85 @@ BOOST_AUTO_TEST_CASE(btck_chainman_tests)
|
||||
chainman_opts.SetWorkerThreads(4);
|
||||
ChainMan chainman{context, chainman_opts};
|
||||
}
|
||||
|
||||
std::unique_ptr<ChainMan> create_chainman(TestDirectory& test_directory,
|
||||
Context& context)
|
||||
{
|
||||
ChainstateManagerOptions chainman_opts{context, test_directory.m_directory.string(), (test_directory.m_directory / "blocks").string()};
|
||||
auto chainman{std::make_unique<ChainMan>(context, chainman_opts)};
|
||||
return chainman;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(btck_chainman_mainnet_tests)
|
||||
{
|
||||
auto mainnet_test_directory{TestDirectory{"mainnet_test_bitcoin_kernel"}};
|
||||
|
||||
auto notifications{std::make_shared<TestKernelNotifications>()};
|
||||
auto context{create_context(notifications, ChainType::MAINNET)};
|
||||
auto chainman{create_chainman(mainnet_test_directory, context)};
|
||||
|
||||
{
|
||||
// Process an invalid block
|
||||
auto raw_block = hex_string_to_byte_vec("012300");
|
||||
BOOST_CHECK_THROW(Block{raw_block}, std::runtime_error);
|
||||
}
|
||||
{
|
||||
// Process an empty block
|
||||
auto raw_block = hex_string_to_byte_vec("");
|
||||
BOOST_CHECK_THROW(Block{raw_block}, std::runtime_error);
|
||||
}
|
||||
|
||||
// mainnet block 1
|
||||
auto raw_block = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000");
|
||||
Block block{raw_block};
|
||||
TransactionView tx{block.GetTransaction(block.CountTransactions() - 1)};
|
||||
BOOST_CHECK_EQUAL(tx.CountInputs(), 1);
|
||||
Transaction tx2 = tx;
|
||||
BOOST_CHECK_EQUAL(tx2.CountInputs(), 1);
|
||||
for (auto transaction : block.Transactions()) {
|
||||
BOOST_CHECK_EQUAL(transaction.CountInputs(), 1);
|
||||
}
|
||||
auto output_counts = *(block.Transactions() | std::views::transform([](const auto& tx) {
|
||||
return tx.CountOutputs();
|
||||
})).begin();
|
||||
BOOST_CHECK_EQUAL(output_counts, 1);
|
||||
bool new_block = false;
|
||||
BOOST_CHECK(chainman->ProcessBlock(block, &new_block));
|
||||
BOOST_CHECK(new_block);
|
||||
|
||||
// If we try to validate it again, it should be a duplicate
|
||||
BOOST_CHECK(chainman->ProcessBlock(block, &new_block));
|
||||
BOOST_CHECK(!new_block);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
|
||||
{
|
||||
auto test_directory{TestDirectory{"regtest_test_bitcoin_kernel"}};
|
||||
|
||||
auto notifications{std::make_shared<TestKernelNotifications>()};
|
||||
auto context{create_context(notifications, ChainType::REGTEST)};
|
||||
|
||||
// Validate 206 regtest blocks in total.
|
||||
// Stop halfway to check that it is possible to continue validating starting
|
||||
// from prior state.
|
||||
const size_t mid{REGTEST_BLOCK_DATA.size() / 2};
|
||||
|
||||
{
|
||||
auto chainman{create_chainman(test_directory, context)};
|
||||
for (size_t i{0}; i < mid; i++) {
|
||||
Block block{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[i])};
|
||||
bool new_block{false};
|
||||
BOOST_CHECK(chainman->ProcessBlock(block, &new_block));
|
||||
BOOST_CHECK(new_block);
|
||||
}
|
||||
}
|
||||
|
||||
auto chainman{create_chainman(test_directory, context)};
|
||||
|
||||
for (size_t i{mid}; i < REGTEST_BLOCK_DATA.size(); i++) {
|
||||
Block block{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[i])};
|
||||
bool new_block{false};
|
||||
BOOST_CHECK(chainman->ProcessBlock(block, &new_block));
|
||||
BOOST_CHECK(new_block);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user