Repository: bitcoin/bitcoin
File: src/kernel/bitcoinkernel.h (C/C++)

Code before:
 */
BITCOINKERNEL_API void btck_block_hash_to_bytes(
    const btck_BlockHash* block_hash, unsigned char output[32]) BITCOINKERNEL_ARG_NONNULL(1, 2);

/**
 * Destroy the block hash.
 */
BITCOINKERNEL_API void btck_block_hash_destroy(btck_BlockHash* block_hash);

///@}

/**
 * @name Block Header
 * Functions for working with block headers.
 */
///@{

/**
 * @brief Create a btck_BlockHeader from serialized data.
 *
 * @param[in] raw_block_header      Non-null, serialized header data (80 bytes)
 * @param[in] raw_block_header_len  Length of serialized header (must be 80)
 * @return                          Block Header, or null on error.
 */
BITCOINKERNEL_API btck_BlockHeader* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_create(
    const void* raw_block_header, size_t raw_block_header_len) BITCOINKERNEL_ARG_NONNULL(1);

/**
 * @brief Copy a btck_BlockHeader.
 *
 * @param[in] header    Non-null btck_BlockHeader.
 * @return              Copied btck_BlockHeader.
 */
BITCOINKERNEL_API btck_BlockHeader* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_copy(
    const btck_BlockHeader* header) BITCOINKERNEL_ARG_NONNULL(1);

/**
 * @brief Get the btck_BlockHash.
 *
 * @param[in] header    Non-null header
 * @return              Block hash.
 */
BITCOINKERNEL_API btck_BlockHash* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_get_hash(
    const btck_BlockHeader* header) BITCOINKERNEL_ARG_NONNULL(1);

/**
 * @brief Get the previous btck_BlockHash from btck_BlockHeader.
 *
 * @param[in] header    Non-null btck_BlockHeader
 * @return              Previous btck_BlockHash (view, do not destroy)
 */

Review comment (suggestion):
`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:

<details>
<summary>diff</summary>

```diff
diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
--- a/src/kernel/bitcoinkernel.cpp
+++ b/src/kernel/bitcoinkernel.cpp
@@ -1310,10 +1310,10 @@ int btck_chain_contains(const btck_Chain* chain, const btck_BlockTreeEntry* entr
     return btck_Chain::get(chain).Contains(&btck_BlockTreeEntry::get(entry)) ? 1 : 0;
 }
 
-btck_BlockHeader* btck_block_header_create(const void* raw_block_header, size_t raw_block_header_len)
+btck_BlockHeader* btck_block_header_create(const unsigned char raw_block_header[80])
 {
     auto header{std::make_unique<CBlockHeader>()};
-    DataStream stream{std::span{reinterpret_cast<const std::byte*>(raw_block_header), raw_block_header_len}};
+    DataStream stream{std::span<const unsigned char>{raw_block_header, 80}};
 
     try {
         stream >> *header;
diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h
index 24990772d8..10985ce7c2 100644
--- a/src/kernel/bitcoinkernel.h
+++ b/src/kernel/bitcoinkernel.h
@@ -1643,12 +1643,11 @@ BITCOINKERNEL_API void btck_block_hash_destroy(btck_BlockHash* block_hash);
 /**
  * @brief Create a btck_BlockHeader from serialized data.
  *
- * @param[in] raw_block_header      Non-null, serialized header data (80 bytes)
- * @param[in] raw_block_header_len  Length of serialized header (must be 80)
- * @return                          Block Header, or null on error.
+ * @param[in] raw_block_header  Non-null, serialized header data
+ * @return                      Block Header, or null on error.
  */
 BITCOINKERNEL_API btck_BlockHeader* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_create(
-    const void* raw_block_header, size_t raw_block_header_len) BITCOINKERNEL_ARG_NONNULL(1);
+    const unsigned char raw_block_header[80]) BITCOINKERNEL_ARG_NONNULL(1);
 
 /**
  * @brief Copy a btck_BlockHeader.
diff --git a/src/kernel/bitcoinkernel_wrapper.h b/src/kernel/bitcoinkernel_wrapper.h
index f7b956824f..2c92507a2d 100644
--- a/src/kernel/bitcoinkernel_wrapper.h
+++ b/src/kernel/bitcoinkernel_wrapper.h
@@ -755,14 +755,14 @@ public:
 class BlockHeader : public Handle<btck_BlockHeader, btck_block_header_copy, btck_block_header_destroy>, public BlockHeaderApi<BlockHeader>
 {
 public:
-    explicit BlockHeader(std::span<const std::byte> raw_header)
-        : Handle{btck_block_header_create(reinterpret_cast<const unsigned char*>(raw_header.data()), raw_header.size())} {}
+    explicit BlockHeader(const std::array<std::byte, 80>& raw_header)
+        : Handle{btck_block_header_create(reinterpret_cast<const unsigned char*>(raw_header.data()))} {}
 
     BlockHeader(const BlockHeaderView& view)
         : Handle{view} {}
 
     BlockHeader(btck_BlockHeader* ptr)
         : Handle{ptr} {}
 };
 
 class Block : public Handle<btck_Block, btck_block_copy, btck_block_destroy>
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index 01865ee9d7..239680f06d 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -61,6 +61,18 @@ std::vector<std::byte> hex_string_to_byte_vec(std::string_view hex)
     return bytes;
 }
 
+std::array<std::byte, 80> hex_string_to_block_header(std::string_view hex)
+{
+    auto vec = hex_string_to_byte_vec(hex);
+    if (vec.size() != 80) {
+        throw std::invalid_argument("Block header must be exactly 80 bytes");
+    }
+
+    std::array<std::byte, 80> header;
+    std::copy(vec.begin(), vec.end(), header.begin());
+    return header;
+}
+
 std::string byte_span_to_hex_string_reversed(std::span<const std::byte> bytes)
 {
     std::ostringstream oss;
@@ -585,18 +597,14 @@ BOOST_AUTO_TEST_CASE(btck_context_tests)
 BOOST_AUTO_TEST_CASE(btck_block_header_tests)
 {
     // Block header format: version(4) + prev_hash(32) + merkle_root(32) + timestamp(4) + bits(4) + nonce(4) = 80 bytes
-    BlockHeader header_0{hex_string_to_byte_vec("00e07a26beaaeee2e71d7eb19279545edbaf15de0999983626ec00000000000000000000579cf78b65229bfb93f4a11463af2eaa5ad91780f27f5d147a423bea5f7e4cdf2a47e268b4dd01173a9662ee")};
+    BlockHeader header_0{hex_string_to_block_header("00e07a26beaaeee2e71d7eb19279545edbaf15de0999983626ec00000000000000000000579cf78b65229bfb93f4a11463af2eaa5ad91780f27f5d147a423bea5f7e4cdf2a47e268b4dd01173a9662ee")};
     BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(header_0.Hash().ToBytes()), "00000000000000000000325c7e14a4ee3b4fcb2343089a839287308a0ddbee4f");
-    BlockHeader header_1{hex_string_to_byte_vec("00c00020e7cb7b4de21d26d55bd384017b8bb9333ac3b2b55bed00000000000000000000d91b4484f801b99f03d36b9d26cfa83420b67f81da12d7e6c1e7f364e743c5ba9946e268b4dd011799c8533d")};
+    BlockHeader header_1{hex_string_to_block_header("00c00020e7cb7b4de21d26d55bd384017b8bb9333ac3b2b55bed00000000000000000000d91b4484f801b99f03d36b9d26cfa83420b67f81da12d7e6c1e7f364e743c5ba9946e268b4dd011799c8533d")};
     CheckHandle(header_0, header_1);
 
-    // Test error handling for invalid data
-    BOOST_CHECK_THROW(BlockHeader{hex_string_to_byte_vec("00")}, std::runtime_error);
-
     // Test all header field accessors using mainnet block 1
     // Version: 1, Timestamp: 1231469665 (Jan 3, 2009), Bits: 0x1d00ffff, Nonce: 2573394689
-    auto mainnet_block_1_header = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299");
-    BlockHeader header{mainnet_block_1_header};
+    BlockHeader header{hex_string_to_block_header("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299")};
     BOOST_CHECK_EQUAL(header.Version(), 1);
     BOOST_CHECK_EQUAL(header.Timestamp(), 1231469665);
     BOOST_CHECK_EQUAL(header.Bits(), 0x1d00ffff);

```

</details>

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. 

Code after:
    const btck_Coin* coin) BITCOINKERNEL_ARG_NONNULL(1);

/**
 * @brief Return the transaction output of a coin. The returned pointer is
 * unowned and only valid for the lifetime of the coin.
 *
 * @param[in] coin Non-null.
 * @return         A transaction output pointer.
 */
BITCOINKERNEL_API const btck_TransactionOutput* BITCOINKERNEL_WARN_UNUSED_RESULT btck_coin_get_output(
    const btck_Coin* coin) BITCOINKERNEL_ARG_NONNULL(1);

/**
 * Destroy the coin.
 */
BITCOINKERNEL_API void btck_coin_destroy(btck_Coin* coin);

///@}

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

/**
 * @brief Create a block hash from its raw data.
 */
BITCOINKERNEL_API btck_BlockHash* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_hash_create(
    const unsigned char block_hash[32]) BITCOINKERNEL_ARG_NONNULL(1);

/**
 * @brief Check if two block hashes are equal.
 *
 * @param[in] hash1 Non-null.
 * @param[in] hash2 Non-null.
 * @return          0 if the block hashes are not equal.
 */
BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_hash_equals(
    const btck_BlockHash* hash1, const btck_BlockHash* hash2) BITCOINKERNEL_ARG_NONNULL(1, 2);

/**
 * @brief Copy a block hash.
 *
 * @param[in] block_hash Non-null.
 * @return               The copied block hash.
 */
BITCOINKERNEL_API btck_BlockHash* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_hash_copy(
    const btck_BlockHash* block_hash) BITCOINKERNEL_ARG_NONNULL(1);

/**
 * @brief Serializes the block hash to bytes.