diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp index e115518df81..753489373d3 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 e1f99b53151..62db5daa3d8 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 249f98a3684..53ecee3e460 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 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 0e21cc5689f..fcb614c58a9 100644 --- a/src/test/kernel/test_kernel.cpp +++ b/src/test/kernel/test_kernel.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -129,9 +130,14 @@ public: class TestValidationInterface : public ValidationInterface { public: + std::optional> 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);