diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp index 0a315ff39b3..92700d162c2 100644 --- a/src/kernel/bitcoinkernel.cpp +++ b/src/kernel/bitcoinkernel.cpp @@ -498,6 +498,7 @@ struct btck_TransactionSpentOutputs : Handle {}; struct btck_BlockHash : Handle {}; struct btck_TransactionInput : Handle {}; +struct btck_WitnessStack : Handle {}; struct btck_TransactionOutPoint: Handle {}; struct btck_Txid: Handle {}; struct btck_PrecomputedTransactionData : Handle {}; @@ -709,11 +710,38 @@ uint32_t btck_transaction_input_get_sequence(const btck_TransactionInput* input) return btck_TransactionInput::get(input).nSequence; } +const btck_WitnessStack* btck_transaction_input_get_witness_stack(const btck_TransactionInput* input) +{ + return btck_WitnessStack::ref(&btck_TransactionInput::get(input).scriptWitness); +} + void btck_transaction_input_destroy(btck_TransactionInput* input) { delete input; } +size_t btck_witness_stack_count_items(const btck_WitnessStack* witness_stack) +{ + return btck_WitnessStack::get(witness_stack).stack.size(); +} + +int btck_witness_stack_get_item_at(const btck_WitnessStack* witness_stack, size_t index, btck_WriteBytes writer, void* user_data) +{ + const auto& stack{btck_WitnessStack::get(witness_stack).stack}; + assert(index < stack.size()); + return writer(stack[index].data(), stack[index].size(), user_data); +} + +btck_WitnessStack* btck_witness_stack_copy(const btck_WitnessStack* witness_stack) +{ + return btck_WitnessStack::copy(witness_stack); +} + +void btck_witness_stack_destroy(btck_WitnessStack* witness_stack) +{ + delete witness_stack; +} + btck_TransactionOutPoint* btck_transaction_out_point_copy(const btck_TransactionOutPoint* out_point) { return btck_TransactionOutPoint::copy(out_point); diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h index 6ce65a04378..09b1caf08b9 100644 --- a/src/kernel/bitcoinkernel.h +++ b/src/kernel/bitcoinkernel.h @@ -298,10 +298,17 @@ typedef struct btck_BlockHash btck_BlockHash; /** * Opaque data structure for holding a transaction input. * - * Holds information on the @ref btck_TransactionOutPoint held within. + * Holds information on the @ref btck_TransactionOutPoint, @ref btck_WitnessStack and script_sig held within. */ typedef struct btck_TransactionInput btck_TransactionInput; +/** + * Opaque data structure for holding a witness stack. + * + * Holds a sequence of witness stack items. + */ +typedef struct btck_WitnessStack btck_WitnessStack; + /** * Opaque data structure for holding a transaction out point. * @@ -1680,6 +1687,16 @@ BITCOINKERNEL_API const btck_TransactionOutPoint* btck_transaction_input_get_out BITCOINKERNEL_API uint32_t btck_transaction_input_get_sequence( const btck_TransactionInput* transaction_input) BITCOINKERNEL_ARG_NONNULL(1); +/** + * @brief Get the witness stack of a transaction input. The returned witness + * stack is not owned and depends on the lifetime of the transaction input. + * + * @param[in] transaction_input Non-null. + * @return The witness stack. + */ +BITCOINKERNEL_API const btck_WitnessStack* btck_transaction_input_get_witness_stack( + const btck_TransactionInput* transaction_input) BITCOINKERNEL_ARG_NONNULL(1); + /** * Destroy the transaction input. */ @@ -1687,6 +1704,53 @@ BITCOINKERNEL_API void btck_transaction_input_destroy(btck_TransactionInput* tra ///@} +/** @name Witness Stack + * Functions for working with witness stacks. + */ +///@{ + +/** + * @brief Return the number of items in a witness stack. + * + * @param[in] witness_stack Non-null. + * @return The number of witness stack items. + */ +BITCOINKERNEL_API size_t btck_witness_stack_count_items( + const btck_WitnessStack* witness_stack) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * @brief Serialize a witness stack item at a given index through the passed in + * callback. + * + * @param[in] witness_stack Non-null. + * @param[in] index Index of the item in the witness stack. + * @param[in] writer Non-null, function pointer for writing bytes. + * @param[in] user_data Nullable, passed back through the writer callback. + * @return The return value of the writer. + * @pre index < btck_witness_stack_count_items(witness_stack) + */ +BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_witness_stack_get_item_at( + const btck_WitnessStack* witness_stack, + size_t index, + btck_WriteBytes writer, + void* user_data) BITCOINKERNEL_ARG_NONNULL(1, 3); + +/** + * @brief Copy a witness stack. + * + * @param[in] witness_stack Non-null. + * @return The copied witness stack. + */ +BITCOINKERNEL_API btck_WitnessStack* BITCOINKERNEL_WARN_UNUSED_RESULT btck_witness_stack_copy( + const btck_WitnessStack* witness_stack) BITCOINKERNEL_ARG_NONNULL(1); + +/** + * Destroy the witness stack. + */ +BITCOINKERNEL_API void btck_witness_stack_destroy(btck_WitnessStack* witness_stack); + +///@} + /** @name Transaction Out Point * Functions for working with transaction out points. */ diff --git a/src/kernel/bitcoinkernel_wrapper.h b/src/kernel/bitcoinkernel_wrapper.h index ff7899d1d69..f84a06edd2d 100644 --- a/src/kernel/bitcoinkernel_wrapper.h +++ b/src/kernel/bitcoinkernel_wrapper.h @@ -564,6 +564,49 @@ public: : Handle(view) {} }; +template +class WitnessStackApi +{ +private: + auto impl() const + { + return static_cast(this)->get(); + } + + friend Derived; + WitnessStackApi() = default; + +public: + size_t CountItems() const + { + return btck_witness_stack_count_items(impl()); + } + + std::vector GetItem(size_t index) const + { + struct Item { const btck_WitnessStack* stack; size_t index; }; + Item item{impl(), index}; + return write_bytes(&item, +[](const Item* c, btck_WriteBytes w, void* ud) { + return btck_witness_stack_get_item_at(c->stack, c->index, w, ud); + }); + } + + MAKE_RANGE_METHOD(Items, Derived, &WitnessStackApi::CountItems, &WitnessStackApi::GetItem, *static_cast(this)) +}; + +class WitnessStackView : public View, public WitnessStackApi +{ +public: + explicit WitnessStackView(const btck_WitnessStack* ptr) : View{ptr} {} +}; + +class WitnessStack : public Handle, public WitnessStackApi +{ +public: + WitnessStack(const WitnessStackView& view) + : Handle(view) {} +}; + template class TransactionInputApi { @@ -586,6 +629,12 @@ public: { return btck_transaction_input_get_sequence(impl()); } + + WitnessStackView GetWitnessStack() const + { + return WitnessStackView{btck_transaction_input_get_witness_stack(impl())}; + } + }; class TransactionInputView : public View, public TransactionInputApi diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp index e6d215dc578..8dc63fd606b 100644 --- a/src/test/kernel/test_kernel.cpp +++ b/src/test/kernel/test_kernel.cpp @@ -513,6 +513,28 @@ BOOST_AUTO_TEST_CASE(btck_transaction_input) OutPoint point_0 = input_0.OutPoint(); OutPoint point_1 = input_1.OutPoint(); CheckHandle(point_0, point_1); + + WitnessStackView ws_0 = input_0.GetWitnessStack(); + BOOST_CHECK_EQUAL(ws_0.CountItems(), 0); + BOOST_CHECK(ws_0.Items().empty()); + + // P2WSH input: OP_0, sig, sig, redeem_script (0, 71, 71, 105 bytes); no scriptSig. + Transaction segwit_tx{hex_string_to_byte_vec("010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000")}; + TransactionInputView segwit_input = segwit_tx.GetInput(0); + WitnessStackView ws = segwit_input.GetWitnessStack(); + BOOST_CHECK_EQUAL(ws.CountItems(), 4); + BOOST_CHECK(ws.GetItem(0).empty()); + BOOST_CHECK(ws.GetItem(1) == hex_string_to_byte_vec("30440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc01")); + BOOST_CHECK(ws.GetItem(2) == hex_string_to_byte_vec("30440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd435501")); + BOOST_CHECK(ws.GetItem(3) == hex_string_to_byte_vec("52210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae")); + auto items = ws.Items(); + BOOST_CHECK_EQUAL(items.size(), 4); + for (size_t i = 0; i < items.size(); ++i) { + BOOST_CHECK(items[i] == ws.GetItem(i)); + } + WitnessStack owned_ws_0{ws_0}; + WitnessStack owned_ws{ws}; + CheckHandle(owned_ws_0, owned_ws); } BOOST_AUTO_TEST_CASE(btck_precomputed_txdata) {