kernel: Add Block Header serialization method
What changed, and why it matters
This commit adds a new public library function that converts a Bitcoin block header into its standard 80-byte wire format. It is a straightforward feature addition with no visible bug fix or security change. The code uses safe, fixed-size buffers and existing well-tested serialization routines.
No security action required. Review as normal API addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces btck_block_header_to_bytes() in the bitcoinkernel C API, plus a C++ wrapper ToBytes() and unit tests. It serializes a CBlockHeader into an 80-byte output using SpanWriter over a std::span, catching any exceptions and returning an error code. The implementation delegates to the existing consensus/network serialization operator<< for CBlockHeader. No memory allocations are performed by the serializer itself, and the output buffer size is fixed and caller-provided.
Changed components
src/kernel/bitcoinkernel.cppsrc/kernel/bitcoinkernel.hsrc/kernel/bitcoinkernel_wrapper.hsrc/test/kernel/test_kernel.cppInspect captured patch +40 / −0
diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
index ea646cd5..b456f41a 100644
--- a/src/kernel/bitcoinkernel.cpp
+++ b/src/kernel/bitcoinkernel.cpp
@@ -1390,6 +1390,16 @@ uint32_t btck_block_header_get_nonce(const btck_BlockHeader* header)
return btck_BlockHeader::get(header).nNonce;
}
+int btck_block_header_to_bytes(const btck_BlockHeader* header, unsigned char output[80])
+{
+ try {
+ SpanWriter{std::as_writable_bytes(std::span{output, 80})} << btck_BlockHeader::get(header);
+ return 0;
+ } catch (...) {
+ return -1;
+ }
+}
+
void btck_block_header_destroy(btck_BlockHeader* header)
{
delete header;
diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h
index 5427e776..f3a85cf2 100644
--- a/src/kernel/bitcoinkernel.h
+++ b/src/kernel/bitcoinkernel.h
@@ -1768,6 +1768,17 @@ BITCOINKERNEL_API int32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_get
BITCOINKERNEL_API uint32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_get_nonce(
const btck_BlockHeader* header) BITCOINKERNEL_ARG_NONNULL(1);
+/**
+ * @brief Serializes the btck_BlockHeader to bytes.
+ * This is consensus serialization that is also used for the P2P network.
+ *
+ * @param[in] header Non-null.
+ * @param[out] output The serialized block header (80 bytes).
+ * @return 0 on success.
+ */
+BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_to_bytes(
+ const btck_BlockHeader* header, unsigned char output[80]) BITCOINKERNEL_ARG_NONNULL(1, 2);
+
/**
* Destroy the btck_BlockHeader.
*/
diff --git a/src/kernel/bitcoinkernel_wrapper.h b/src/kernel/bitcoinkernel_wrapper.h
index 064d0dd1..63ca62b0 100644
--- a/src/kernel/bitcoinkernel_wrapper.h
+++ b/src/kernel/bitcoinkernel_wrapper.h
@@ -746,6 +746,16 @@ public:
{
return btck_block_header_get_nonce(impl());
}
+
+ std::array<std::byte, 80> ToBytes() const
+ {
+ std::array<std::byte, 80> header;
+ int res{btck_block_header_to_bytes(impl(), reinterpret_cast<unsigned char*>(header.data()))};
+ if (res != 0) {
+ throw std::runtime_error("Failed to serialize block header");
+ }
+ return header;
+ }
};
class BlockHeaderView : public View<btck_BlockHeader>, public BlockHeaderApi<BlockHeaderView>
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index 5a380065..5958b8d4 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -682,6 +682,10 @@ BOOST_AUTO_TEST_CASE(btck_block_header_tests)
auto prev_hash = header.PrevHash();
BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(prev_hash.ToBytes()), "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
+ // Test round-trip serialization of block header
+ auto header_roundtrip{BlockHeader{header.ToBytes()}};
+ check_equal(header_roundtrip.ToBytes(), mainnet_block_1_header);
+
auto raw_block = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000");
Block block{raw_block};
BlockHeader block_header{block.GetHeader()};
@@ -690,6 +694,11 @@ BOOST_AUTO_TEST_CASE(btck_block_header_tests)
BOOST_CHECK_EQUAL(block_header.Bits(), 0x1d00ffff);
BOOST_CHECK_EQUAL(block_header.Nonce(), 2573394689);
BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(block_header.Hash().ToBytes()), "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048");
+
+ // Verify header from block serializes to first 80 bytes of raw block
+ auto block_header_bytes = block_header.ToBytes();
+ BOOST_CHECK_EQUAL(block_header_bytes.size(), 80);
+ check_equal(block_header_bytes, std::span<const std::byte>(raw_block.data(), 80));
}
BOOST_AUTO_TEST_CASE(btck_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.