kernel: Add block hash type and block tree utility functions to C header

Introduce btck_BlockHash as a type-safe identifier for a block. Adds
functions to retrieve block tree entries by hash or height, get block
hashes and heights from entries. access the genesis block, and check if
blocks are in the active chain.
This commit is contained in:
TheCharlatan
2024-06-05 22:05:40 +02:00
parent f5d5d1213c
commit 5eec7fa96a
4 changed files with 364 additions and 1 deletions

View File

@@ -634,6 +634,39 @@ void chainman_reindex_test(TestDirectory& test_directory)
std::vector<std::string> import_files;
BOOST_CHECK(chainman->ImportBlocks(import_files));
// Sanity check some block retrievals
auto chain{chainman->GetChain()};
BOOST_CHECK_THROW(chain.GetByHeight(1000), std::runtime_error);
auto genesis_index{chain.Genesis()};
BOOST_CHECK(!genesis_index.GetPrevious());
auto genesis_block_raw{chainman->ReadBlock(genesis_index).value().ToBytes()};
auto first_index{chain.GetByHeight(0)};
auto first_block_raw{chainman->ReadBlock(genesis_index).value().ToBytes()};
check_equal(genesis_block_raw, first_block_raw);
auto height{first_index.GetHeight()};
BOOST_CHECK_EQUAL(height, 0);
auto next_index{chain.GetByHeight(first_index.GetHeight() + 1)};
BOOST_CHECK(chain.Contains(next_index));
auto next_block_data{chainman->ReadBlock(next_index).value().ToBytes()};
auto tip_index{chain.Tip()};
auto tip_block_data{chainman->ReadBlock(tip_index).value().ToBytes()};
auto second_index{chain.GetByHeight(1)};
auto second_block{chainman->ReadBlock(second_index).value()};
auto second_block_data{second_block.ToBytes()};
auto second_height{second_index.GetHeight()};
BOOST_CHECK_EQUAL(second_height, 1);
check_equal(next_block_data, tip_block_data);
check_equal(next_block_data, second_block_data);
auto second_hash{second_index.GetHash()};
auto another_second_index{chainman->GetBlockTreeEntry(second_hash)};
BOOST_CHECK(another_second_index);
auto another_second_height{another_second_index->GetHeight()};
auto second_block_hash{second_block.GetHash()};
check_equal(second_block_hash.ToBytes(), second_hash.ToBytes());
BOOST_CHECK_EQUAL(second_height, another_second_height);
}
void chainman_reindex_chainstate_test(TestDirectory& test_directory)
@@ -722,6 +755,21 @@ BOOST_AUTO_TEST_CASE(btck_chainman_mainnet_tests)
chainman_reindex_chainstate_test(test_directory);
}
BOOST_AUTO_TEST_CASE(btck_block_hash_tests)
{
std::array<std::byte, 32> test_hash;
std::array<std::byte, 32> test_hash_2;
for (int i = 0; i < 32; ++i) {
test_hash[i] = static_cast<std::byte>(i);
test_hash_2[i] = static_cast<std::byte>(i + 1);
}
BlockHash block_hash{test_hash};
BlockHash block_hash_2{test_hash_2};
BOOST_CHECK(block_hash != block_hash_2);
BOOST_CHECK(block_hash == block_hash);
CheckHandle(block_hash, block_hash_2);
}
BOOST_AUTO_TEST_CASE(btck_chainman_in_memory_tests)
{
auto in_memory_test_directory{TestDirectory{"in-memory_test_bitcoin_kernel"}};
@@ -824,6 +872,31 @@ BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
}
}
CheckRange(chain.Entries(), chain.CountEntries());
for (const BlockTreeEntry entry : chain.Entries()) {
std::optional<Block> block{chainman->ReadBlock(entry)};
if (block) {
for (const TransactionView transaction : block->Transactions()) {
for (const TransactionOutputView output : transaction.Outputs()) {
// skip data carrier outputs
if ((unsigned char)output.GetScriptPubkey().ToBytes()[0] == 0x6a) {
continue;
}
BOOST_CHECK_GT(output.Amount(), 1);
}
}
}
}
int32_t count{0};
for (const auto entry : chain.Entries()) {
BOOST_CHECK_EQUAL(entry.GetHeight(), count);
++count;
}
BOOST_CHECK_EQUAL(count, chain.CountEntries());
std::filesystem::remove_all(test_directory.m_directory / "blocks" / "blk00000.dat");
BOOST_CHECK(!chainman->ReadBlock(tip_2).has_value());
std::filesystem::remove_all(test_directory.m_directory / "blocks" / "rev00000.dat");