diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 71d0f3e95cc..ae215c198ed 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -57,7 +57,7 @@ public: std::optional m_expected_valid_block = std::nullopt; - void BlockChecked(const Block block, const BlockValidationState state) override + void BlockChecked(Block block, BlockValidationStateView state) override { auto mode{state.GetValidationMode()}; switch (mode) { diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp index 8c7abef59f1..03698e72b92 100644 --- a/src/kernel/bitcoinkernel.cpp +++ b/src/kernel/bitcoinkernel.cpp @@ -496,6 +496,7 @@ struct btck_TransactionInput : Handle {}; struct btck_TransactionOutPoint: Handle {}; struct btck_Txid: Handle {}; struct btck_PrecomputedTransactionData : Handle {}; +struct btck_BlockHeader: Handle {}; btck_Transaction* btck_transaction_create(const void* raw_transaction, size_t raw_transaction_len) { @@ -885,6 +886,21 @@ const btck_BlockTreeEntry* btck_block_tree_entry_get_previous(const btck_BlockTr return btck_BlockTreeEntry::ref(btck_BlockTreeEntry::get(entry).pprev); } +btck_BlockValidationState* btck_block_validation_state_create() +{ + return btck_BlockValidationState::create(); +} + +btck_BlockValidationState* btck_block_validation_state_copy(const btck_BlockValidationState* state) +{ + return btck_BlockValidationState::copy(state); +} + +void btck_block_validation_state_destroy(btck_BlockValidationState* state) +{ + delete state; +} + btck_ValidationMode btck_block_validation_state_get_validation_mode(const btck_BlockValidationState* block_validation_state_) { auto& block_validation_state = btck_BlockValidationState::get(block_validation_state_); @@ -1029,6 +1045,12 @@ const btck_BlockTreeEntry* btck_chainstate_manager_get_block_tree_entry_by_hash( return btck_BlockTreeEntry::ref(block_index); } +const btck_BlockTreeEntry* btck_chainstate_manager_get_best_entry(const btck_ChainstateManager* chainstate_manager) +{ + auto& chainman = *btck_ChainstateManager::get(chainstate_manager).m_chainman; + return btck_BlockTreeEntry::ref(WITH_LOCK(chainman.GetMutex(), return chainman.m_best_header)); +} + void btck_chainstate_manager_destroy(btck_ChainstateManager* chainman) { { @@ -1097,6 +1119,12 @@ const btck_Transaction* btck_block_get_transaction_at(const btck_Block* block, s return btck_Transaction::ref(&btck_Block::get(block)->vtx[index]); } +btck_BlockHeader* btck_block_get_header(const btck_Block* block) +{ + const auto& block_ptr = btck_Block::get(block); + return btck_BlockHeader::create(static_cast(*block_ptr)); +} + int btck_block_to_bytes(const btck_Block* block, btck_WriteBytes writer, void* user_data) { try { @@ -1128,6 +1156,11 @@ btck_Block* btck_block_read(const btck_ChainstateManager* chainman, const btck_B return btck_Block::create(block); } +btck_BlockHeader* btck_block_tree_entry_get_block_header(const btck_BlockTreeEntry* entry) +{ + return btck_BlockHeader::create(btck_BlockTreeEntry::get(entry).GetBlockHeader()); +} + int32_t btck_block_tree_entry_get_height(const btck_BlockTreeEntry* entry) { return btck_BlockTreeEntry::get(entry).nHeight; @@ -1264,6 +1297,22 @@ int btck_chainstate_manager_process_block( return result ? 0 : -1; } +int btck_chainstate_manager_process_block_header( + btck_ChainstateManager* chainstate_manager, + const btck_BlockHeader* header, + btck_BlockValidationState* state) +{ + try { + auto& chainman = btck_ChainstateManager::get(chainstate_manager).m_chainman; + auto result = chainman->ProcessNewBlockHeaders({&btck_BlockHeader::get(header), 1}, /*min_pow_checked=*/true, btck_BlockValidationState::get(state), /*ppindex=*/nullptr); + + return result ? 0 : -1; + } catch (const std::exception& e) { + LogError("Failed to process block header: %s", e.what()); + return -1; + } +} + const btck_Chain* btck_chainstate_manager_get_active_chain(const btck_ChainstateManager* chainman) { return btck_Chain::ref(&WITH_LOCK(btck_ChainstateManager::get(chainman).m_chainman->GetMutex(), return btck_ChainstateManager::get(chainman).m_chainman->ActiveChain())); @@ -1286,3 +1335,61 @@ int btck_chain_contains(const btck_Chain* chain, const btck_BlockTreeEntry* entr LOCK(::cs_main); 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) +{ + if (raw_block_header == nullptr && raw_block_header_len != 0) { + return nullptr; + } + auto header{std::make_unique()}; + DataStream stream{std::span{reinterpret_cast(raw_block_header), raw_block_header_len}}; + + try { + stream >> *header; + } catch (...) { + LogError("Block header decode failed."); + return nullptr; + } + + return btck_BlockHeader::ref(header.release()); +} + +btck_BlockHeader* btck_block_header_copy(const btck_BlockHeader* header) +{ + return btck_BlockHeader::copy(header); +} + +btck_BlockHash* btck_block_header_get_hash(const btck_BlockHeader* header) +{ + return btck_BlockHash::create(btck_BlockHeader::get(header).GetHash()); +} + +const btck_BlockHash* btck_block_header_get_prev_hash(const btck_BlockHeader* header) +{ + return btck_BlockHash::ref(&btck_BlockHeader::get(header).hashPrevBlock); +} + +uint32_t btck_block_header_get_timestamp(const btck_BlockHeader* header) +{ + return btck_BlockHeader::get(header).nTime; +} + +uint32_t btck_block_header_get_bits(const btck_BlockHeader* header) +{ + return btck_BlockHeader::get(header).nBits; +} + +int32_t btck_block_header_get_version(const btck_BlockHeader* header) +{ + return btck_BlockHeader::get(header).nVersion; +} + +uint32_t btck_block_header_get_nonce(const btck_BlockHeader* header) +{ + return btck_BlockHeader::get(header).nNonce; +} + +void btck_block_header_destroy(btck_BlockHeader* header) +{ + delete header; +} diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h index 53a760cd2ff..a7f09dd3a40 100644 --- a/src/kernel/bitcoinkernel.h +++ b/src/kernel/bitcoinkernel.h @@ -292,8 +292,18 @@ typedef struct btck_TransactionOutPoint btck_TransactionOutPoint; */ typedef struct btck_PrecomputedTransactionData btck_PrecomputedTransactionData; +/** + * Opaque data structure for holding a btck_Txid. + * + * This is a type-safe identifier for a transaction. + */ typedef struct btck_Txid btck_Txid; +/** + * Opaque data structure for holding a btck_BlockHeader. + */ +typedef struct btck_BlockHeader btck_BlockHeader; + /** Current sync state passed to tip changed callbacks. */ typedef uint8_t btck_SynchronizationState; #define btck_SynchronizationState_INIT_REINDEX ((btck_SynchronizationState)(0)) @@ -958,6 +968,15 @@ BITCOINKERNEL_API void btck_context_destroy(btck_Context* context); BITCOINKERNEL_API const btck_BlockTreeEntry* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_tree_entry_get_previous( const btck_BlockTreeEntry* block_tree_entry) BITCOINKERNEL_ARG_NONNULL(1); +/** + * @brief Return the btck_BlockHeader associated with this entry. + * + * @param[in] block_tree_entry Non-null. + * @return btck_BlockHeader. + */ +BITCOINKERNEL_API btck_BlockHeader* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_tree_entry_get_block_header( + const btck_BlockTreeEntry* block_tree_entry) BITCOINKERNEL_ARG_NONNULL(1); + /** * @brief Return the height of a certain block tree entry. * @@ -1083,6 +1102,29 @@ BITCOINKERNEL_API void btck_chainstate_manager_options_destroy(btck_ChainstateMa BITCOINKERNEL_API btck_ChainstateManager* BITCOINKERNEL_WARN_UNUSED_RESULT btck_chainstate_manager_create( const btck_ChainstateManagerOptions* chainstate_manager_options) BITCOINKERNEL_ARG_NONNULL(1); +/** + * @brief Get the btck_BlockTreeEntry whose associated btck_BlockHeader has the most + * known cumulative proof of work. + * + * @param[in] chainstate_manager Non-null. + * @return The btck_BlockTreeEntry. + */ +BITCOINKERNEL_API const btck_BlockTreeEntry* BITCOINKERNEL_WARN_UNUSED_RESULT btck_chainstate_manager_get_best_entry( + const btck_ChainstateManager* chainstate_manager) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * @brief Processes and validates the provided btck_BlockHeader. + * + * @param[in] chainstate_manager Non-null. + * @param[in] header Non-null btck_BlockHeader to be validated. + * @param[out] block_validation_state The result of the btck_BlockHeader validation. + * @return 0 if btck_BlockHeader processing completed successfully, non-zero on error. + */ +BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_chainstate_manager_process_block_header( + btck_ChainstateManager* chainstate_manager, + const btck_BlockHeader* header, + btck_BlockValidationState* block_validation_state) BITCOINKERNEL_ARG_NONNULL(1, 2, 3); + /** * @brief Triggers the start of a reindex if the wipe options were previously * set for the chainstate manager. Can also import an array of existing block @@ -1214,6 +1256,17 @@ 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 Get the btck_BlockHeader from the block. + * + * Creates a new btck_BlockHeader object from the block's header data. + * + * @param[in] block Non-null btck_Block + * @return btck_BlockHeader. + */ +BITCOINKERNEL_API btck_BlockHeader* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_get_header( + const btck_Block* block) BITCOINKERNEL_ARG_NONNULL(1); + /** * @brief Calculate and return the hash of a block. * @@ -1251,17 +1304,37 @@ BITCOINKERNEL_API void btck_block_destroy(btck_Block* block); ///@{ /** - * Returns the validation mode from an opaque block validation state pointer. + * Create a new btck_BlockValidationState. + */ +BITCOINKERNEL_API btck_BlockValidationState* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_validation_state_create(); + +/** + * Returns the validation mode from an opaque btck_BlockValidationState pointer. */ BITCOINKERNEL_API btck_ValidationMode btck_block_validation_state_get_validation_mode( const btck_BlockValidationState* block_validation_state) BITCOINKERNEL_ARG_NONNULL(1); /** - * Returns the validation result from an opaque block validation state pointer. + * Returns the validation result from an opaque btck_BlockValidationState pointer. */ BITCOINKERNEL_API btck_BlockValidationResult btck_block_validation_state_get_block_validation_result( const btck_BlockValidationState* block_validation_state) BITCOINKERNEL_ARG_NONNULL(1); +/** + * @brief Copies the btck_BlockValidationState. + * + * @param[in] block_validation_state Non-null. + * @return The copied btck_BlockValidationState. + */ +BITCOINKERNEL_API btck_BlockValidationState* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_validation_state_copy( + const btck_BlockValidationState* block_validation_state) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * Destroy the btck_BlockValidationState. + */ +BITCOINKERNEL_API void btck_block_validation_state_destroy( + btck_BlockValidationState* block_validation_state) BITCOINKERNEL_ARG_NONNULL(1); + ///@} /** @name Chain @@ -1615,6 +1688,93 @@ 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 btck_BlockHeader, 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); + +/** + * @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 btck_BlockHash. + */ +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. The returned hash + * is unowned and only valid for the lifetime of the btck_BlockHeader. + * + * @param[in] header Non-null btck_BlockHeader + * @return Previous btck_BlockHash + */ +BITCOINKERNEL_API const btck_BlockHash* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_get_prev_hash( + const btck_BlockHeader* header) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * @brief Get the timestamp from btck_BlockHeader. + * + * @param[in] header Non-null btck_BlockHeader + * @return Block timestamp (Unix epoch seconds) + */ +BITCOINKERNEL_API uint32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_get_timestamp( + const btck_BlockHeader* header) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * @brief Get the nBits difficulty target from btck_BlockHeader. + * + * @param[in] header Non-null btck_BlockHeader + * @return Difficulty target (compact format) + */ +BITCOINKERNEL_API uint32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_get_bits( + const btck_BlockHeader* header) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * @brief Get the version from btck_BlockHeader. + * + * @param[in] header Non-null btck_BlockHeader + * @return Block version + */ +BITCOINKERNEL_API int32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_get_version( + const btck_BlockHeader* header) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * @brief Get the nonce from btck_BlockHeader. + * + * @param[in] header Non-null btck_BlockHeader + * @return Nonce + */ +BITCOINKERNEL_API uint32_t BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_get_nonce( + const btck_BlockHeader* header) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * Destroy the btck_BlockHeader. + */ +BITCOINKERNEL_API void btck_block_header_destroy(btck_BlockHeader* header); + +///@} + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/src/kernel/bitcoinkernel_wrapper.h b/src/kernel/bitcoinkernel_wrapper.h index 5c1b8d07eb3..064d0dd14b9 100644 --- a/src/kernel/bitcoinkernel_wrapper.h +++ b/src/kernel/bitcoinkernel_wrapper.h @@ -685,7 +685,7 @@ public: } }; -class BlockHashView: public View, public BlockHashApi +class BlockHashView : public View, public BlockHashApi { public: explicit BlockHashView(const btck_BlockHash* ptr) : View{ptr} {} @@ -704,6 +704,69 @@ public: : Handle{view} {} }; +template +class BlockHeaderApi +{ +private: + auto impl() const + { + return static_cast(this)->get(); + } + + friend Derived; + BlockHeaderApi() = default; + +public: + BlockHash Hash() const + { + return BlockHash{btck_block_header_get_hash(impl())}; + } + + BlockHashView PrevHash() const + { + return BlockHashView{btck_block_header_get_prev_hash(impl())}; + } + + uint32_t Timestamp() const + { + return btck_block_header_get_timestamp(impl()); + } + + uint32_t Bits() const + { + return btck_block_header_get_bits(impl()); + } + + int32_t Version() const + { + return btck_block_header_get_version(impl()); + } + + uint32_t Nonce() const + { + return btck_block_header_get_nonce(impl()); + } +}; + +class BlockHeaderView : public View, public BlockHeaderApi +{ +public: + explicit BlockHeaderView(const btck_BlockHeader* ptr) : View{ptr} {} +}; + +class BlockHeader : public Handle, public BlockHeaderApi +{ +public: + explicit BlockHeader(std::span raw_header) + : Handle{btck_block_header_create(reinterpret_cast(raw_header.data()), raw_header.size())} {} + + BlockHeader(const BlockHeaderView& view) + : Handle{view} {} + + BlockHeader(btck_BlockHeader* header) + : Handle{header} {} +}; + class Block : public Handle { public: @@ -731,6 +794,11 @@ public: return BlockHash{btck_block_get_hash(get())}; } + BlockHeader GetHeader() const + { + return BlockHeader{btck_block_get_header(get())}; + } + std::vector ToBytes() const { return write_bytes(get(), btck_block_to_bytes); @@ -809,6 +877,11 @@ public: { return BlockHashView{btck_block_tree_entry_get_block_hash(get())}; } + + BlockHeader GetHeader() const + { + return BlockHeader{btck_block_tree_entry_get_block_header(get())}; + } }; class KernelNotifications @@ -831,36 +904,50 @@ public: virtual void FatalErrorHandler(std::string_view error) {} }; -class BlockValidationState +template +class BlockValidationStateApi { private: - const btck_BlockValidationState* m_state; + auto impl() const + { + return static_cast(this)->get(); + } + + friend Derived; + BlockValidationStateApi() = default; public: - BlockValidationState(const btck_BlockValidationState* state) : m_state{state} {} - - BlockValidationState(const BlockValidationState&) = delete; - BlockValidationState& operator=(const BlockValidationState&) = delete; - BlockValidationState(BlockValidationState&&) = delete; - BlockValidationState& operator=(BlockValidationState&&) = delete; - ValidationMode GetValidationMode() const { - return static_cast(btck_block_validation_state_get_validation_mode(m_state)); + return static_cast(btck_block_validation_state_get_validation_mode(impl())); } BlockValidationResult GetBlockValidationResult() const { - return static_cast(btck_block_validation_state_get_block_validation_result(m_state)); + return static_cast(btck_block_validation_state_get_block_validation_result(impl())); } }; +class BlockValidationStateView : public View, public BlockValidationStateApi +{ +public: + explicit BlockValidationStateView(const btck_BlockValidationState* ptr) : View{ptr} {} +}; + +class BlockValidationState : public Handle, public BlockValidationStateApi +{ +public: + explicit BlockValidationState() : Handle{btck_block_validation_state_create()} {} + + BlockValidationState(const BlockValidationStateView& view) : Handle{view} {} +}; + class ValidationInterface { public: virtual ~ValidationInterface() = default; - virtual void BlockChecked(Block block, const BlockValidationState state) {} + virtual void BlockChecked(Block block, BlockValidationStateView state) {} virtual void PowValidBlock(BlockTreeEntry entry, Block block) {} @@ -918,7 +1005,7 @@ public: btck_ValidationInterfaceCallbacks{ .user_data = heap_vi.release(), .user_data_destroy = +[](void* user_data) { delete static_cast(user_data); }, - .block_checked = +[](void* user_data, btck_Block* block, const btck_BlockValidationState* state) { (*static_cast(user_data))->BlockChecked(Block{block}, BlockValidationState{state}); }, + .block_checked = +[](void* user_data, btck_Block* block, const btck_BlockValidationState* state) { (*static_cast(user_data))->BlockChecked(Block{block}, BlockValidationStateView{state}); }, .pow_valid_block = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast(user_data))->PowValidBlock(BlockTreeEntry{entry}, Block{block}); }, .block_connected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast(user_data))->BlockConnected(Block{block}, BlockTreeEntry{entry}); }, .block_disconnected = +[](void* user_data, btck_Block* block, const btck_BlockTreeEntry* entry) { (*static_cast(user_data))->BlockDisconnected(Block{block}, BlockTreeEntry{entry}); }, @@ -1130,6 +1217,11 @@ public: return res == 0; } + bool ProcessBlockHeader(const BlockHeader& header, BlockValidationState& state) + { + return btck_chainstate_manager_process_block_header(get(), header.get(), state.get()) == 0; + } + ChainView GetChain() const { return ChainView{btck_chainstate_manager_get_active_chain(get())}; @@ -1142,6 +1234,11 @@ public: return entry; } + BlockTreeEntry GetBestEntry() const + { + return btck_chainstate_manager_get_best_entry(get()); + } + std::optional ReadBlock(const BlockTreeEntry& entry) const { auto block{btck_block_read(get(), entry.get())}; diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp index 07f1d3cfc62..0b86cf8090e 100644 --- a/src/test/kernel/test_kernel.cpp +++ b/src/test/kernel/test_kernel.cpp @@ -145,7 +145,7 @@ class TestValidationInterface : public ValidationInterface public: std::optional> m_expected_valid_block = std::nullopt; - void BlockChecked(Block block, const BlockValidationState state) override + void BlockChecked(Block block, BlockValidationStateView state) override { if (m_expected_valid_block.has_value()) { auto ser_block{block.ToBytes()}; @@ -659,6 +659,39 @@ 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")}; + BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(header_0.Hash().ToBytes()), "00000000000000000000325c7e14a4ee3b4fcb2343089a839287308a0ddbee4f"); + BlockHeader header_1{hex_string_to_byte_vec("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); + BOOST_CHECK_THROW(BlockHeader{hex_string_to_byte_vec("")}, std::runtime_error); + + // Test all header field accessors using mainnet block 1 + auto mainnet_block_1_header = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299"); + BlockHeader header{mainnet_block_1_header}; + BOOST_CHECK_EQUAL(header.Version(), 1); + BOOST_CHECK_EQUAL(header.Timestamp(), 1231469665); + BOOST_CHECK_EQUAL(header.Bits(), 0x1d00ffff); + BOOST_CHECK_EQUAL(header.Nonce(), 2573394689); + BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(header.Hash().ToBytes()), "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"); + auto prev_hash = header.PrevHash(); + BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(prev_hash.ToBytes()), "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); + + auto raw_block = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000"); + Block block{raw_block}; + BlockHeader block_header{block.GetHeader()}; + BOOST_CHECK_EQUAL(block_header.Version(), 1); + BOOST_CHECK_EQUAL(block_header.Timestamp(), 1231469665); + 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"); +} + BOOST_AUTO_TEST_CASE(btck_block) { Block block{hex_string_to_byte_vec(REGTEST_BLOCK_DATA[0])}; @@ -819,8 +852,13 @@ void chainman_mainnet_validation_test(TestDirectory& test_directory) // mainnet block 1 auto raw_block = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000"); Block block{raw_block}; + BlockHeader header{block.GetHeader()}; TransactionView tx{block.GetTransaction(block.CountTransactions() - 1)}; BOOST_CHECK_EQUAL(byte_span_to_hex_string_reversed(tx.Txid().ToBytes()), "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"); + BOOST_CHECK_EQUAL(header.Version(), 1); + BOOST_CHECK_EQUAL(header.Timestamp(), 1231469665); + BOOST_CHECK_EQUAL(header.Bits(), 0x1d00ffff); + BOOST_CHECK_EQUAL(header.Nonce(), 2573394689); BOOST_CHECK_EQUAL(tx.CountInputs(), 1); Transaction tx2 = tx; BOOST_CHECK_EQUAL(tx2.CountInputs(), 1); @@ -960,6 +998,23 @@ BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests) auto notifications{std::make_shared()}; auto context{create_context(notifications, ChainType::REGTEST)}; + { + auto chainman{create_chainman(test_directory, false, false, false, false, context)}; + for (const auto& data : REGTEST_BLOCK_DATA) { + Block block{hex_string_to_byte_vec(data)}; + BlockHeader header = block.GetHeader(); + BlockValidationState state{}; + BOOST_CHECK(state.GetBlockValidationResult() == BlockValidationResult::UNSET); + BOOST_CHECK(chainman->ProcessBlockHeader(header, state)); + BOOST_CHECK(state.GetValidationMode() == ValidationMode::VALID); + BlockTreeEntry entry{*chainman->GetBlockTreeEntry(header.Hash())}; + BOOST_CHECK(!chainman->GetChain().Contains(entry)); + BlockTreeEntry best_entry{chainman->GetBestEntry()}; + BlockHash hash{entry.GetHash()}; + BOOST_CHECK(hash == best_entry.GetHeader().Hash()); + } + } + // Validate 206 regtest blocks in total. // Stop halfway to check that it is possible to continue validating starting // from prior state.