kernel: Add function for copying block data to C header
What changed, and why it matters
This commit adds a new public API function that lets users serialize a Bitcoin block to raw bytes through a callback. It also adds a C++ wrapper method and uses both in existing tests to verify that the validation interface reports the same block bytes that were originally submitted. There is no security fix here—this is a feature addition for testing and API completeness.
No security action required. Review as normal code-quality/API addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces btck_block_to_bytes() in the kernel C API, which streams a block’s consensus serialization (with witness) through a user-supplied btck_WriteBytes callback. A corresponding Block::ToBytes() wrapper is added, and the kernel test suite uses it inside TestValidationInterface::BlockChecked to compare the serialized block against the expected raw bytes. The test is also expanded to cover an invalid block case. No memory-safety, consensus, or network vulnerabilities are introduced or patched.
Changed components
src/kernel/bitcoinkernel.cppsrc/kernel/bitcoinkernel.hsrc/kernel/bitcoinkernel_wrapper.hsrc/test/kernel/test_kernel.cppInspect captured patch +55 / −8
diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
index e115518d..75348937 100644
--- a/src/kernel/bitcoinkernel.cpp
+++ b/src/kernel/bitcoinkernel.cpp
@@ -967,6 +967,17 @@ const btck_Transaction* btck_block_get_transaction_at(const btck_Block* block, s
return btck_Transaction::ref(&btck_Block::get(block)->vtx[index]);
}
+int btck_block_to_bytes(const btck_Block* block, btck_WriteBytes writer, void* user_data)
+{
+ try {
+ WriterStream ws{writer, user_data};
+ ws << TX_WITH_WITNESS(*btck_Block::get(block));
+ return 0;
+ } catch (...) {
+ return -1;
+ }
+}
+
void btck_block_destroy(btck_Block* block)
{
delete block;
diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h
index e1f99b53..62db5daa 100644
--- a/src/kernel/bitcoinkernel.h
+++ b/src/kernel/bitcoinkernel.h
@@ -249,6 +249,11 @@ typedef void (*btck_ValidationInterfacePoWValidBlock)(void* user_data, btck_Bloc
typedef void (*btck_ValidationInterfaceBlockConnected)(void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry);
typedef void (*btck_ValidationInterfaceBlockDisconnected)(void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry);
+/**
+ * Function signature for serializing data.
+ */
+typedef int (*btck_WriteBytes)(const void* bytes, size_t size, void* userdata);
+
/**
* Whether a validated data structure is valid, invalid, or an error was
* encountered during processing.
@@ -389,11 +394,6 @@ typedef uint8_t btck_ChainType;
#define btck_ChainType_SIGNET ((btck_ChainType)(3))
#define btck_ChainType_REGTEST ((btck_ChainType)(4))
-/**
- * Function signature for serializing data.
- */
-typedef int (*btck_WriteBytes)(const void* bytes, size_t size, void* userdata);
-
/** @name Transaction
* Functions for working with transactions.
*/
@@ -983,6 +983,21 @@ BITCOINKERNEL_API size_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_count_trans
BITCOINKERNEL_API const btck_Transaction* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_get_transaction_at(
const btck_Block* block, size_t transaction_index) BITCOINKERNEL_ARG_NONNULL(1);
+/**
+ * @brief Serializes the block through the passed in callback to bytes.
+ * This is consensus serialization that is also used for the P2P network.
+ *
+ * @param[in] block Non-null.
+ * @param[in] writer Non-null, callback to a write bytes function.
+ * @param[in] user_data Holds a user-defined opaque structure that will be
+ * passed back through the writer callback.
+ * @return 0 on success.
+ */
+BITCOINKERNEL_API int btck_block_to_bytes(
+ const btck_Block* block,
+ btck_WriteBytes writer,
+ void* user_data) BITCOINKERNEL_ARG_NONNULL(1, 2);
+
/**
* Destroy the block.
*/
diff --git a/src/kernel/bitcoinkernel_wrapper.h b/src/kernel/bitcoinkernel_wrapper.h
index 249f98a3..53ecee3e 100644
--- a/src/kernel/bitcoinkernel_wrapper.h
+++ b/src/kernel/bitcoinkernel_wrapper.h
@@ -548,6 +548,11 @@ public:
}
MAKE_RANGE_METHOD(Transactions, Block, &Block::CountTransactions, &Block::GetTransaction, *this)
+
+ std::vector<std::byte> ToBytes() const
+ {
+ return write_bytes(get(), btck_block_to_bytes);
+ }
};
inline void logging_disable()
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index 0e21cc56..fcb614c5 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -16,6 +16,7 @@
#include <filesystem>
#include <iostream>
#include <memory>
+#include <optional>
#include <random>
#include <ranges>
#include <span>
@@ -129,9 +130,14 @@ public:
class TestValidationInterface : public ValidationInterface
{
public:
+ std::optional<std::vector<std::byte>> m_expected_valid_block = std::nullopt;
+
void BlockChecked(Block block, const BlockValidationState state) override
{
- std::cout << "Block checked: ";
+ if (m_expected_valid_block.has_value()) {
+ auto ser_block{block.ToBytes()};
+ check_equal(m_expected_valid_block.value(), ser_block);
+ }
auto mode{state.GetValidationMode()};
switch (mode) {
@@ -545,8 +551,8 @@ 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_100{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[100])};
+ CheckHandle(block, block_100);
Block block_tx{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[205])};
CheckRange(block_tx.Transactions(), block_tx.CountTransactions());
}
@@ -673,10 +679,20 @@ void chainman_mainnet_validation_test(TestDirectory& test_directory)
return tx.CountOutputs();
})).begin();
BOOST_CHECK_EQUAL(output_counts, 1);
+
+ validation_interface->m_expected_valid_block.emplace(raw_block);
+ auto ser_block{block.ToBytes()};
+ check_equal(ser_block, raw_block);
bool new_block = false;
BOOST_CHECK(chainman->ProcessBlock(block, &new_block));
BOOST_CHECK(new_block);
+ validation_interface->m_expected_valid_block = std::nullopt;
+ new_block = false;
+ Block invalid_block{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[REGTEST_BLOCK_DATA.size() - 1])};
+ BOOST_CHECK(!chainman->ProcessBlock(invalid_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);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.