must be 80 might suggest there is an enforcement of the length somewhere while for example in the test_kernel.cpp appending extra bytes to mainnet_block_1_header won’t break the test and extra bytes would just be discarded.
Might be helpful to have the enforcement in btck_block_header_create or revise the doc.
Alternatively we might be able to be explicit about the length in the API (like BlockHash) and have the following changes, adapting both the C API and C++ wrapper:
0diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
1--- a/src/kernel/bitcoinkernel.cpp
2+++ b/src/kernel/bitcoinkernel.cpp
3@@ -1310,10 +1310,10 @@ int btck_chain_contains(const btck_Chain* chain, const btck_BlockTreeEntry* entr
4 return btck_Chain::get(chain).Contains(&btck_BlockTreeEntry::get(entry)) ? 1 : 0;
5 }
6
7-btck_BlockHeader* btck_block_header_create(const void* raw_block_header, size_t raw_block_header_len)
8+btck_BlockHeader* btck_block_header_create(const unsigned char raw_block_header[80])
9 {
10 auto header{std::make_unique<CBlockHeader>()};
11- DataStream stream{std::span{reinterpret_cast<const std::byte*>(raw_block_header), raw_block_header_len}};
12+ DataStream stream{std::span<const unsigned char>{raw_block_header, 80}};
13
14 try {
15 stream >> *header;
16diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h
17index 24990772d8..10985ce7c2 100644
18--- a/src/kernel/bitcoinkernel.h
19+++ b/src/kernel/bitcoinkernel.h
20@@ -1643,12 +1643,11 @@ BITCOINKERNEL_API void btck_block_hash_destroy(btck_BlockHash* block_hash);
21 /**
22 * [@brief](/bitcoin-bitcoin/contributor/brief/) Create a btck_BlockHeader from serialized data.
23 *
24- * [@param](/bitcoin-bitcoin/contributor/param/)[in] raw_block_header Non-null, serialized header data (80 bytes)
25- * [@param](/bitcoin-bitcoin/contributor/param/)[in] raw_block_header_len Length of serialized header (must be 80)
26- * [@return](/bitcoin-bitcoin/contributor/return/) Block Header, or null on error.
27+ * [@param](/bitcoin-bitcoin/contributor/param/)[in] raw_block_header Non-null, serialized header data
28+ * [@return](/bitcoin-bitcoin/contributor/return/) Block Header, or null on error.
29 */
30 BITCOINKERNEL_API btck_BlockHeader* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_create(
31- const void* raw_block_header, size_t raw_block_header_len) BITCOINKERNEL_ARG_NONNULL(1);
32+ const unsigned char raw_block_header[80]) BITCOINKERNEL_ARG_NONNULL(1);
33
34 /**
35 * [@brief](/bitcoin-bitcoin/contributor/brief/) Copy a btck_BlockHeader.
36diff --git a/src/kernel/bitcoinkernel_wrapper.h b/src/kernel/bitcoinkernel_wrapper.h
37index f7b956824f..2c92507a2d 100644
38--- a/src/kernel/bitcoinkernel_wrapper.h
39+++ b/src/kernel/bitcoinkernel_wrapper.h
40@@ -755,14 +755,14 @@ public:
41 class BlockHeader : public Handle<btck_BlockHeader, btck_block_header_copy, btck_block_header_destroy>, public BlockHeaderApi<BlockHeader>
42 {
43 public:
44- explicit BlockHeader(std::span<const std::byte> raw_header)
45- : Handle{btck_block_header_create(reinterpret_cast<const unsigned char*>(raw_header.data()), raw_header.size())} {}
46+ explicit BlockHeader(const std::array<std::byte, 80>& raw_header)
47+ : Handle{btck_block_header_create(reinterpret_cast<const unsigned char*>(raw_header.data()))} {}
48
49 BlockHeader(const BlockHeaderView& view)
50 : Handle{view} {}
51
52 BlockHeader(btck_BlockHeader* ptr)
53 : Handle{ptr} {}
54 };
55
56 class Block : public Handle<btck_Block, btck_block_copy, btck_block_destroy>
57diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
58index 01865ee9d7..239680f06d 100644
59--- a/src/test/kernel/test_kernel.cpp
60+++ b/src/test/kernel/test_kernel.cpp
61@@ -61,6 +61,18 @@ std::vector<std::byte> hex_string_to_byte_vec(std::string_view hex)
62 return bytes;
63 }
64
65+std::array<std::byte, 80> hex_string_to_block_header(std::string_view hex)
66+{
67+ auto vec = hex_string_to_byte_vec(hex);
68+ if (vec.size() != 80) {
69+ throw std::invalid_argument("Block header must be exactly 80 bytes");
70+ }
71+
72+ std::array<std::byte, 80> header;
73+ std::copy(vec.begin(), vec.end(), header.begin());
74+ return header;
75+}
76+
77 std::string byte_span_to_hex_string_reversed(std::span<const std::byte> bytes)
78 {
79 std::ostringstream oss;
80@@ -585,18 +597,14 @@ BOOST_AUTO_TEST_CASE(btck_context_tests)
81 BOOST_AUTO_TEST_CASE(btck_block_header_tests)
82 {
83 // Block header format: version(4) + prev_hash(32) + merkle_root(32) + timestamp(4) + bits(4) + nonce(4) = 80 bytes
84- BlockHeader header_0{hex_string_to_byte_vec("00e07a26beaaeee2e71d7eb19279545edbaf15de0999983626ec00000000000000000000579cf78b65229bfb93f4a11463af2eaa5ad91780f27f5d147a423bea5f7e4cdf2a47e268b4dd01173a9662ee")};
85+ BlockHeader header_0{hex_string_to_block_header("00e07a26beaaeee2e71d7eb19279545edbaf15de0999983626ec00000000000000000000579cf78b65229bfb93f4a11463af2eaa5ad91780f27f5d147a423bea5f7e4cdf2a47e268b4dd01173a9662ee")};
86 BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(header_0.Hash().ToBytes()), "00000000000000000000325c7e14a4ee3b4fcb2343089a839287308a0ddbee4f");
87- BlockHeader header_1{hex_string_to_byte_vec("00c00020e7cb7b4de21d26d55bd384017b8bb9333ac3b2b55bed00000000000000000000d91b4484f801b99f03d36b9d26cfa83420b67f81da12d7e6c1e7f364e743c5ba9946e268b4dd011799c8533d")};
88+ BlockHeader header_1{hex_string_to_block_header("00c00020e7cb7b4de21d26d55bd384017b8bb9333ac3b2b55bed00000000000000000000d91b4484f801b99f03d36b9d26cfa83420b67f81da12d7e6c1e7f364e743c5ba9946e268b4dd011799c8533d")};
89 CheckHandle(header_0, header_1);
90
91- // Test error handling for invalid data
92- BOOST_CHECK_THROW(BlockHeader{hex_string_to_byte_vec("00")}, std::runtime_error);
93-
94 // Test all header field accessors using mainnet block 1
95 // Version: 1, Timestamp: 1231469665 (Jan 3, 2009), Bits: 0x1d00ffff, Nonce: 2573394689
96- auto mainnet_block_1_header = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299");
97- BlockHeader header{mainnet_block_1_header};
98+ BlockHeader header{hex_string_to_block_header("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299")};
99 BOOST_CHECK_EQUAL(header.Version(), 1);
100 BOOST_CHECK_EQUAL(header.Timestamp(), 1231469665);
101 BOOST_CHECK_EQUAL(header.Bits(), 0x1d00ffff);
If the last approach is taken then I think my first comment (https://github.com/bitcoin/bitcoin/pull/33822#discussion_r2623138029) is not applicable anymore.