diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp index 152f778b8b1..52090c4408d 100644 --- a/src/kernel/bitcoinkernel.cpp +++ b/src/kernel/bitcoinkernel.cpp @@ -500,6 +500,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 {}; @@ -707,11 +708,44 @@ 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); +} + +int btck_transaction_input_get_script_sig(const btck_TransactionInput* input, btck_WriteBytes writer, void* user_data) +{ + const auto& script_sig{btck_TransactionInput::get(input).scriptSig}; + return writer(script_sig.data(), script_sig.size(), user_data); +} + 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 6d646b30f19..86ef2e2c398 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. * @@ -1691,6 +1698,30 @@ 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); + +/** + * @brief Serialize the script sig of a transaction input through the passed + * in callback. + * + * @param[in] transaction_input Non-null. + * @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. + */ +BITCOINKERNEL_API int BITCOINKERNEL_WARN_UNUSED_RESULT btck_transaction_input_get_script_sig( + const btck_TransactionInput* transaction_input, + btck_WriteBytes writer, + void* user_data) BITCOINKERNEL_ARG_NONNULL(1, 2); + /** * Destroy the transaction input. */ @@ -1698,6 +1729,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 b00328af3c0..5f251d309c5 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,16 @@ public: { return btck_transaction_input_get_sequence(impl()); } + + WitnessStackView GetWitnessStack() const + { + return WitnessStackView{btck_transaction_input_get_witness_stack(impl())}; + } + + std::vector GetScriptSig() const + { + return write_bytes(impl(), btck_transaction_input_get_script_sig); + } }; class TransactionInputView : public View, public TransactionInputApi diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp index 689732e170c..927d2b16e8a 100644 --- a/src/test/kernel/test_kernel.cpp +++ b/src/test/kernel/test_kernel.cpp @@ -503,6 +503,33 @@ 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()); + + // P2PKH: DER sig + compressed pubkey push. + BOOST_CHECK(input_0.GetScriptSig() == hex_string_to_byte_vec("473044022004893432347f39beaa280e99da595681ddb20fc45010176897e6e055d716dbfa022040a9e46648a5d10c33ef7cee5e6cf4b56bd513eae3ae044f0039824b02d0f44c012102982331a52822fd9b62e9b5d120da1d248558fac3da3a3c51cd7d9c8ad3da760e")); + BOOST_CHECK(input_1.GetScriptSig() == hex_string_to_byte_vec("473044022068bcedc7fe39c9f21ad318df2c2da62c2dc9522a89c28c8420ff9d03d2e6bf7b0220132afd752754e5cb1ea2fd0ed6a38ec666781e34b0e93dc9a08f2457842cf5660121033aeb9c079ea3e08ea03556182ab520ce5c22e6b0cb95cee6435ee17144d860cd")); + + // 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_CHECK(segwit_input.GetScriptSig().empty()); } BOOST_AUTO_TEST_CASE(btck_precomputed_txdata) {