Skip to content

Commit

Permalink
kernel: Add block index utility functions to C header
Browse files Browse the repository at this point in the history
Adds further functions useful for traversing the block index and
retrieving block information.

This includes getting the block height and hash.
  • Loading branch information
TheCharlatan committed Jan 15, 2025
1 parent 572e008 commit 1bf825f
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 1 deletion.
70 changes: 69 additions & 1 deletion src/kernel/bitcoinkernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -976,12 +976,58 @@ kernel_BlockIndex* kernel_get_block_index_from_tip(const kernel_Context* context
return reinterpret_cast<kernel_BlockIndex*>(WITH_LOCK(::cs_main, return chainman->ActiveChain().Tip()));
}

kernel_BlockIndex* kernel_get_block_index_from_genesis(const kernel_Context* context_, kernel_ChainstateManager* chainman_)
{
auto chainman{cast_chainstate_manager(chainman_)};
return reinterpret_cast<kernel_BlockIndex*>(WITH_LOCK(::cs_main, return chainman->ActiveChain().Genesis()));
}

kernel_BlockIndex* kernel_get_block_index_from_hash(const kernel_Context* context_, kernel_ChainstateManager* chainman_, kernel_BlockHash* block_hash)
{
auto chainman{cast_chainstate_manager(chainman_)};

auto hash = uint256{Span<const unsigned char>{(*block_hash).hash, 32}};
auto block_index = WITH_LOCK(::cs_main, return chainman->m_blockman.LookupBlockIndex(hash));
if (!block_index) {
LogDebug(BCLog::KERNEL, "A block with the given hash is not indexed.");
return nullptr;
}
return reinterpret_cast<kernel_BlockIndex*>(block_index);
}

kernel_BlockIndex* kernel_get_block_index_from_height(const kernel_Context* context_, kernel_ChainstateManager* chainman_, int height)
{
auto chainman{cast_chainstate_manager(chainman_)};

LOCK(cs_main);

if (height < 0 || height > chainman->ActiveChain().Height()) {
LogDebug(BCLog::KERNEL, "Block height is out of range.");
return nullptr;
}
return reinterpret_cast<kernel_BlockIndex*>(chainman->ActiveChain()[height]);
}

kernel_BlockIndex* kernel_get_next_block_index(const kernel_Context* context_, kernel_ChainstateManager* chainman_, const kernel_BlockIndex* block_index_)
{
const auto block_index{cast_const_block_index(block_index_)};
auto chainman{cast_chainstate_manager(chainman_)};

auto next_block_index{WITH_LOCK(::cs_main, return chainman->ActiveChain().Next(block_index))};

if (!next_block_index) {
LogTrace(BCLog::KERNEL, "The block index is the tip of the current chain, it does not have a next.");
}

return reinterpret_cast<kernel_BlockIndex*>(next_block_index);
}

kernel_BlockIndex* kernel_get_previous_block_index(const kernel_BlockIndex* block_index_)
{
const CBlockIndex* block_index{cast_const_block_index(block_index_)};

if (!block_index->pprev) {
LogInfo("Genesis block has no previous.");
LogTrace(BCLog::KERNEL, "The block index is the genesis, it has no previous.");
return nullptr;
}

Expand Down Expand Up @@ -1069,6 +1115,28 @@ kernel_TransactionOutput* kernel_get_undo_output_by_index(const kernel_BlockUndo
return reinterpret_cast<kernel_TransactionOutput*>(prevout);
}

int32_t kernel_block_index_get_height(const kernel_BlockIndex* block_index_)
{
auto block_index{cast_const_block_index(block_index_)};
return block_index->nHeight;
}

kernel_BlockHash* kernel_block_index_get_block_hash(const kernel_BlockIndex* block_index_)
{
auto block_index{cast_const_block_index(block_index_)};
if (block_index->phashBlock == nullptr) {
return nullptr;
}
auto block_hash = new kernel_BlockHash{};
std::memcpy(block_hash->hash, block_index->phashBlock->begin(), sizeof(*block_index->phashBlock));
return block_hash;
}

void kernel_block_hash_destroy(kernel_BlockHash* hash)
{
if (hash) delete hash;
}

kernel_ScriptPubkey* kernel_copy_script_pubkey_from_output(kernel_TransactionOutput* output_)
{
auto output{cast_transaction_output(output_)};
Expand Down
97 changes: 97 additions & 0 deletions src/kernel/bitcoinkernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,13 @@ typedef enum {
kernel_CHAIN_TYPE_REGTEST,
} kernel_ChainType;

/**
* A type-safe block identifier.
*/
typedef struct {
unsigned char hash[32];
} kernel_BlockHash;

/**
* Convenience struct for holding serialized data.
*/
Expand Down Expand Up @@ -1049,6 +1056,63 @@ kernel_BlockIndex* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_get_block_index_from_
kernel_ChainstateManager* chainstate_manager
) BITCOINKERNEL_ARG_NONNULL(1, 2);

/**
* @brief Get the block index entry of the genesis block.
*
* @param[in] context Non-null.
* @param[in] chainstate_manager Non-null.
* @return The block index of the genesis block, or null on error.
*/
kernel_BlockIndex* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_get_block_index_from_genesis(
const kernel_Context* context,
kernel_ChainstateManager* chainstate_manager
) BITCOINKERNEL_ARG_NONNULL(1, 2);

/**
* @brief Retrieve a block index by its block hash.
*
* @param[in] context Non-null.
* @param[in] chainstate_manager Non-null.
* @param[in] block_hash Non-null.
* @return The block index of the block with the passed in hash, or null on error.
*/
kernel_BlockIndex* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_get_block_index_from_hash(
const kernel_Context* context,
kernel_ChainstateManager* chainstate_manager,
kernel_BlockHash* block_hash
) BITCOINKERNEL_ARG_NONNULL(1, 2, 3);

/**
* @brief Retrieve a block index by its height in the currently active chain.
* Once retrieved there is no guarantee that it remains in the active chain.
*
* @param[in] context Non-null.
* @param[in] chainstate_manager Non-null.
* @param[in] block_height Height in the chain of the to be retrieved block index.
* @return The block index at a certain height in the currently active chain, or null on error.
*/
kernel_BlockIndex* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_get_block_index_from_height(
const kernel_Context* context,
kernel_ChainstateManager* chainstate_manager,
int block_height
) BITCOINKERNEL_ARG_NONNULL(1, 2);

/**
* @brief Return the next block index in the currently active chain, or null if
* the current block index is the tip, or is not in the currently active
* chain.
*
* @param[in] context Non-null.
* @param[in] block_index Non-null.
* @param[in] chainstate_manager Non-null.
* @return The next block index in the currently active chain, or null on error.
*/
kernel_BlockIndex* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_get_next_block_index(
const kernel_Context* context,
kernel_ChainstateManager* chainstate_manager,
const kernel_BlockIndex* block_index
) BITCOINKERNEL_ARG_NONNULL(1, 2, 3);

/**
* @brief Returns the previous block index in the chain, or null if the current
* block index entry is the genesis block.
Expand All @@ -1060,6 +1124,17 @@ kernel_BlockIndex* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_get_previous_block_in
const kernel_BlockIndex* block_index
) BITCOINKERNEL_ARG_NONNULL(1);

/**
* @brief Return the height of a certain block index.
*
* @param[in] block_index Non-null.
* @return The block height.
*/
int32_t BITCOINKERNEL_WARN_UNUSED_RESULT kernel_block_index_get_height(
const kernel_BlockIndex* block_index
) BITCOINKERNEL_ARG_NONNULL(1);


/**
* @brief Destroy the block index.
*/
Expand Down Expand Up @@ -1134,6 +1209,28 @@ void kernel_block_undo_destroy(kernel_BlockUndo* block_undo);

///@}

/** @name BlockHash
* Functions for working with block hashes.
*/
///@{

/**
* @brief Return the block hash associated with a block index.
*
* @param[in] block_index Non-null.
* @return The block hash.
*/
kernel_BlockHash* BITCOINKERNEL_WARN_UNUSED_RESULT kernel_block_index_get_block_hash(
const kernel_BlockIndex* block_index
) BITCOINKERNEL_ARG_NONNULL(1);

/**
* Destroy the block hash.
*/
void kernel_block_hash_destroy(kernel_BlockHash* block_hash);

///@}

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
Expand Down
47 changes: 47 additions & 0 deletions src/kernel/bitcoinkernel_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,13 @@ class BlockUndo
}
};

struct BlockHashDeleter {
void operator()(kernel_BlockHash* ptr) const
{
kernel_block_hash_destroy(ptr);
}
};

class BlockIndex
{
private:
Expand All @@ -558,6 +565,22 @@ class BlockIndex
return index;
}

int32_t GetHeight() const noexcept
{
if (!m_block_index) {
return -1;
}
return kernel_block_index_get_height(m_block_index.get());
}

std::unique_ptr<kernel_BlockHash, BlockHashDeleter> GetHash() const noexcept
{
if (!m_block_index) {
return nullptr;
}
return std::unique_ptr<kernel_BlockHash, BlockHashDeleter>(kernel_block_index_get_block_hash(m_block_index.get()));
}

operator bool() const noexcept
{
return m_block_index && m_block_index.get();
Expand Down Expand Up @@ -613,6 +636,30 @@ class ChainMan
return kernel_get_block_index_from_tip(m_context.m_context.get(), m_chainman);
}

BlockIndex GetBlockIndexFromGenesis() const noexcept
{
return kernel_get_block_index_from_genesis(m_context.m_context.get(), m_chainman);
}

BlockIndex GetBlockIndexByHash(kernel_BlockHash* block_hash) const noexcept
{
return kernel_get_block_index_from_hash(m_context.m_context.get(), m_chainman, block_hash);
}

std::optional<BlockIndex> GetBlockIndexByHeight(int height) const noexcept
{
auto index{kernel_get_block_index_from_height(m_context.m_context.get(), m_chainman, height)};
if (!index) return std::nullopt;
return index;
}

std::optional<BlockIndex> GetNextBlockIndex(BlockIndex& block_index) const noexcept
{
auto index{kernel_get_next_block_index(m_context.m_context.get(), m_chainman, block_index.m_block_index.get())};
if (!index) return std::nullopt;
return index;
}

std::optional<Block> ReadBlock(BlockIndex& block_index) const noexcept
{
auto block{kernel_read_block_from_disk(m_context.m_context.get(), m_chainman, block_index.m_block_index.get())};
Expand Down
25 changes: 25 additions & 0 deletions src/test/kernel/test_kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,31 @@ void chainman_reindex_test(TestDirectory& test_directory)

std::vector<std::string> import_files;
assert(chainman->ImportBlocks(import_files));

// Sanity check some block retrievals
auto genesis_index{chainman->GetBlockIndexFromGenesis()};
auto genesis_block_raw{chainman->ReadBlock(genesis_index).value().GetBlockData()};
auto first_index{chainman->GetBlockIndexByHeight(0).value()};
auto first_block_raw{chainman->ReadBlock(genesis_index).value().GetBlockData()};
assert(genesis_block_raw == first_block_raw);
auto height{first_index.GetHeight()};
assert(height == 0);

auto next_index{chainman->GetNextBlockIndex(first_index).value()};
auto next_block_string{chainman->ReadBlock(next_index).value().GetBlockData()};
auto tip_index{chainman->GetBlockIndexFromTip()};
auto tip_block_string{chainman->ReadBlock(tip_index).value().GetBlockData()};
auto second_index{chainman->GetBlockIndexByHeight(1).value()};
auto second_block_string{chainman->ReadBlock(second_index).value().GetBlockData()};
auto second_height{second_index.GetHeight()};
assert(second_height == 1);
assert(next_block_string == tip_block_string);
assert(next_block_string == second_block_string);

auto hash{second_index.GetHash()};
auto another_second_index{chainman->GetBlockIndexByHash(hash.get())};
auto another_second_height{another_second_index.GetHeight()};
assert(second_height == another_second_height);
}

void chainman_reindex_chainstate_test(TestDirectory& test_directory)
Expand Down

0 comments on commit 1bf825f

Please sign in to comment.