diff --git a/CHANGES.md b/CHANGES.md index ae4a051db..87e76bba7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,11 +6,15 @@ - PSBT: Allow extracting partially finalized transactions in `wally_psbt_extract` by passing a new `WALLY_PSBT_EXTRACT_OPT_FINAL` flag. +- Allow getting the number of items in a transactions input witness via + `wally_tx_input_get_witness_num_items`/`wally_tx_get_input_witness_num_items`. ### Changed ### Fixed +- tx_input_get_witness now correctly returns 0 bytes written if passed a NULL input. + ## Version 1.0.0 diff --git a/include/wally_transaction_members.h b/include/wally_transaction_members.h index e7d121e10..890f2b578 100644 --- a/include/wally_transaction_members.h +++ b/include/wally_transaction_members.h @@ -16,6 +16,7 @@ WALLY_CORE_API int wally_tx_input_get_txhash(const struct wally_tx_input *tx_inp WALLY_CORE_API int wally_tx_input_get_script(const struct wally_tx_input *tx_input_in, unsigned char *bytes_out, size_t len, size_t *written); WALLY_CORE_API int wally_tx_input_get_script_len(const struct wally_tx_input *tx_input_in, size_t *written); +WALLY_CORE_API int wally_tx_input_get_witness_num_items(const struct wally_tx_input *tx_input_in, size_t *written); WALLY_CORE_API int wally_tx_input_get_witness(const struct wally_tx_input *tx_input_in, size_t index, unsigned char *bytes_out, size_t len, size_t *written); WALLY_CORE_API int wally_tx_input_get_witness_len(const struct wally_tx_input *tx_input_in, size_t index, size_t *written); WALLY_CORE_API int wally_tx_input_get_index(const struct wally_tx_input *tx_input_in, size_t *written); @@ -98,6 +99,7 @@ WALLY_CORE_API int wally_tx_get_input_txhash(const struct wally_tx *tx_in, size_ WALLY_CORE_API int wally_tx_get_input_script(const struct wally_tx *tx_in, size_t index, unsigned char *bytes_out, size_t len, size_t *written); WALLY_CORE_API int wally_tx_get_input_script_len(const struct wally_tx *tx_in, size_t index, size_t *written); +WALLY_CORE_API int wally_tx_get_input_witness_num_items(const struct wally_tx *tx_in, size_t index, size_t *written); WALLY_CORE_API int wally_tx_get_input_witness(const struct wally_tx *tx_in, size_t index, size_t wit_index, unsigned char *bytes_out, size_t len, size_t *written); WALLY_CORE_API int wally_tx_get_input_witness_len(const struct wally_tx *tx_in, size_t index, size_t wit_index, size_t *written); WALLY_CORE_API int wally_tx_get_input_index(const struct wally_tx *tx_in, size_t index, size_t *written); diff --git a/src/swig_java/swig.i b/src/swig_java/swig.i index 4f162682e..269fe049f 100644 --- a/src/swig_java/swig.i +++ b/src/swig_java/swig.i @@ -1017,6 +1017,7 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) { %rename("_tx_get_input_sequence") wally_tx_get_input_sequence; %returns_size_t(_tx_get_input_sequence); %returns_array_(wally_tx_get_input_txhash, 3, 4, SHA256_LEN); +%returns_size_t(wally_tx_get_input_witness_num_items); %rename("_tx_get_input_witness") wally_tx_get_input_witness; %returns_size_t(_tx_get_input_witness); %returns_size_t(wally_tx_get_input_witness_len); @@ -1074,6 +1075,7 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) { %returns_array_(wally_tx_input_get_txhash, 2, 3, WALLY_TXHASH_LEN); %rename("_tx_input_get_witness") wally_tx_input_get_witness; %returns_size_t(_tx_input_get_witness); +%returns_size_t(wally_tx_input_get_witness_num_items); %returns_size_t(wally_tx_input_get_witness_len); %returns_struct(wally_tx_input_init_alloc, wally_tx_input); %rename("tx_input_init") wally_tx_input_init_alloc; diff --git a/src/test/test_transaction.py b/src/test/test_transaction.py index f0e10245c..6eea67880 100644 --- a/src/test/test_transaction.py +++ b/src/test/test_transaction.py @@ -67,7 +67,7 @@ def test_serialization(self): utf8('ff')+TX_FAKE_HEX[2:], TX_FAKE_HEX, TX_WITNESS_HEX ]: - self.assertEqual(WALLY_OK, wally_tx_from_hex(tx_hex, 0 ,tx_out)) + self.assertEqual(WALLY_OK, wally_tx_from_hex(tx_hex, 0, tx_out)) hex_ = utf8(self.tx_serialize_hex(tx_out)) self.assertEqual(tx_hex, hex_) # Check the transaction can be cloned and serializes to the same hex @@ -216,7 +216,6 @@ def remove_and_test(idx): 2, 0xfffffffd, script, script_len, wit, 0) self.assertEqual(ret, WALLY_EINVAL) # Invalid index - def test_witness(self): """Testing functions manipulating witness stacks""" witness = wally_tx_witness_stack() @@ -256,6 +255,29 @@ def test_witness(self): ]: self.assertEqual((WALLY_EINVAL, 0), wally_tx_witness_stack_to_bytes(*args)) + # Witness functions on inputs + out, out_len = make_cbuffer('00' * 128) + tx = pointer(wally_tx()) + self.assertEqual(WALLY_OK, wally_tx_from_hex(TX_WITNESS_HEX, 0, tx)) + for wit_tx, wit_index, expected_len in [ + (None, 0, 0), # NULL tx + (tx, 1, 0), # Invalid input index + (tx, 0, 4), # Valid input index + ]: + # num items + ret, num_items = wally_tx_get_input_witness_num_items(wit_tx, wit_index) + self.assertEqual(ret, WALLY_OK if expected_len else WALLY_EINVAL) + self.assertEqual(num_items, expected_len) + item_index = expected_len - 1 if expected_len else 0 + # item length + ret, item_len = wally_tx_get_input_witness_len(wit_tx, wit_index, item_index) + self.assertEqual(ret, WALLY_OK if expected_len else WALLY_EINVAL) + self.assertTrue(ret == WALLY_EINVAL or item_len > 0) + # item + ret, item_len = wally_tx_get_input_witness(wit_tx, wit_index, item_index, out, out_len) + self.assertEqual(ret, WALLY_OK if expected_len else WALLY_EINVAL) + self.assertTrue(ret == WALLY_EINVAL or item_len > 0) + # Round-trip serialization def check_witness_to_bytes(w, expected, expected_len): out, out_len = make_cbuffer('00' * 64) diff --git a/src/test/util.py b/src/test/util.py index a8d5a14bb..7d215398f 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -953,6 +953,7 @@ class wally_psbt(Structure): ('wally_tx_get_input_txhash', c_int, [POINTER(wally_tx), c_size_t, c_void_p, c_size_t]), ('wally_tx_get_input_witness', c_int, [POINTER(wally_tx), c_size_t, c_size_t, c_void_p, c_size_t, c_size_t_p]), ('wally_tx_get_input_witness_len', c_int, [POINTER(wally_tx), c_size_t, c_size_t, c_size_t_p]), + ('wally_tx_get_input_witness_num_items', c_int, [POINTER(wally_tx), c_size_t, c_size_t_p]), ('wally_tx_get_locktime', c_int, [POINTER(wally_tx), c_size_t_p]), ('wally_tx_get_num_inputs', c_int, [POINTER(wally_tx), c_size_t_p]), ('wally_tx_get_num_outputs', c_int, [POINTER(wally_tx), c_size_t_p]), @@ -985,6 +986,7 @@ class wally_psbt(Structure): ('wally_tx_input_get_txhash', c_int, [POINTER(wally_tx_input), c_void_p, c_size_t]), ('wally_tx_input_get_witness', c_int, [POINTER(wally_tx_input), c_size_t, c_void_p, c_size_t, c_size_t_p]), ('wally_tx_input_get_witness_len', c_int, [POINTER(wally_tx_input), c_size_t, c_size_t_p]), + ('wally_tx_input_get_witness_num_items', c_int, [POINTER(wally_tx_input), c_size_t_p]), ('wally_tx_input_set_blinding_nonce', c_int, [POINTER(wally_tx_input), c_void_p, c_size_t]), ('wally_tx_input_set_entropy', c_int, [POINTER(wally_tx_input), c_void_p, c_size_t]), ('wally_tx_input_set_index', c_int, [POINTER(wally_tx_input), c_uint32]), diff --git a/src/transaction.c b/src/transaction.c index 51cbb3a68..6629256b2 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -3547,6 +3547,16 @@ static int tx_getb_impl(const void *input, GET_TX_B_FIXED(tx_input, txhash, WALLY_TXHASH_LEN, WALLY_TXHASH_LEN) GET_TX_B(tx_input, script, input->script_len) +int wally_tx_input_get_witness_num_items(const struct wally_tx_input *input, size_t *written) +{ + if (written) + *written = 0; + if (!is_valid_tx_input(input) || !written) + return WALLY_EINVAL; + *written = input->witness ? input->witness->num_items : 0; + return WALLY_OK; +} + static const struct wally_tx_witness_item *get_witness_preamble( const struct wally_tx_input *input, size_t index, size_t *written) { @@ -3563,7 +3573,7 @@ int wally_tx_input_get_witness(const struct wally_tx_input *input, size_t index, unsigned char *bytes_out, size_t len, size_t *written) { const struct wally_tx_witness_item *item; - if (!bytes_out || !(item = get_witness_preamble(input, index, written)) || + if (!(item = get_witness_preamble(input, index, written)) || !bytes_out || len < item->witness_len) return WALLY_EINVAL; if (item->witness_len) @@ -3572,10 +3582,6 @@ int wally_tx_input_get_witness(const struct wally_tx_input *input, size_t index, return WALLY_OK; } -GET_TX_I(tx_input, index, size_t) -GET_TX_I(tx_input, sequence, size_t) -GET_TX_I(tx_input, script_len, size_t) - int wally_tx_input_get_witness_len(const struct wally_tx_input *input, size_t index, size_t *written) { @@ -3586,6 +3592,10 @@ int wally_tx_input_get_witness_len(const struct wally_tx_input *input, return WALLY_OK; } +GET_TX_I(tx_input, index, size_t) +GET_TX_I(tx_input, sequence, size_t) +GET_TX_I(tx_input, script_len, size_t) + #ifndef WALLY_ABI_NO_ELEMENTS GET_TX_B_FIXED(tx_input, blinding_nonce, SHA256_LEN, SHA256_LEN) GET_TX_B_FIXED(tx_input, entropy, SHA256_LEN, SHA256_LEN) @@ -3801,6 +3811,11 @@ TX_GET_B_FIXED(input, txhash) TX_GET_I(input, index) TX_GET_I(input, sequence) +int wally_tx_get_input_witness_num_items(const struct wally_tx *tx, size_t index, size_t *written) +{ + return wally_tx_input_get_witness_num_items(tx_get_input(tx, index), written); +} + int wally_tx_get_input_witness(const struct wally_tx *tx, size_t index, size_t wit_index, unsigned char *bytes_out, size_t len, size_t *written) { return wally_tx_input_get_witness(tx_get_input(tx, index), wit_index, bytes_out, len, written); @@ -3910,7 +3925,6 @@ int wally_tx_set_input_witness(const struct wally_tx *tx, size_t index, return wally_tx_input_set_witness(tx_get_input(tx, index), stack); } - int wally_tx_input_set_script(struct wally_tx_input *input, const unsigned char *script, size_t script_len) { diff --git a/src/wasm_package/src/functions.js b/src/wasm_package/src/functions.js index bf3f4d79d..211ed6aa8 100644 --- a/src/wasm_package/src/functions.js +++ b/src/wasm_package/src/functions.js @@ -657,6 +657,7 @@ export const tx_get_input_script_len = wrap('wally_tx_get_input_script_len', [T. export const tx_get_input_sequence = wrap('wally_tx_get_input_sequence', [T.OpaqueRef, T.Int32, T.DestPtr(T.Int32)]); export const tx_get_input_txhash = wrap('wally_tx_get_input_txhash', [T.OpaqueRef, T.Int32, T.DestPtrSized(T.Bytes, C.WALLY_TXHASH_LEN)]); export const tx_get_input_witness_len = wrap('wally_tx_get_input_witness_len', [T.OpaqueRef, T.Int32, T.Int32, T.DestPtr(T.Int32)]); +export const tx_get_input_witness_num_items = wrap('wally_tx_get_input_witness_num_items', [T.OpaqueRef, T.Int32, T.DestPtr(T.Int32)]); export const tx_get_length = wrap('wally_tx_get_length', [T.OpaqueRef, T.Int32, T.DestPtr(T.Int32)]); export const tx_get_locktime = wrap('wally_tx_get_locktime', [T.OpaqueRef, T.DestPtr(T.Int32)]); export const tx_get_num_inputs = wrap('wally_tx_get_num_inputs', [T.OpaqueRef, T.DestPtr(T.Int32)]); @@ -688,6 +689,7 @@ export const tx_input_get_script_len = wrap('wally_tx_input_get_script_len', [T. export const tx_input_get_sequence = wrap('wally_tx_input_get_sequence', [T.OpaqueRef, T.DestPtr(T.Int32)]); export const tx_input_get_txhash = wrap('wally_tx_input_get_txhash', [T.OpaqueRef, T.DestPtrSized(T.Bytes, C.WALLY_TXHASH_LEN)]); export const tx_input_get_witness_len = wrap('wally_tx_input_get_witness_len', [T.OpaqueRef, T.Int32, T.DestPtr(T.Int32)]); +export const tx_input_get_witness_num_items = wrap('wally_tx_input_get_witness_num_items', [T.OpaqueRef, T.DestPtr(T.Int32)]); export const tx_input_init = wrap('wally_tx_input_init_alloc', [T.Bytes, T.Int32, T.Int32, T.Bytes, T.OpaqueRef, T.DestPtrPtr(T.OpaqueRef)]); export const tx_input_set_blinding_nonce = wrap('wally_tx_input_set_blinding_nonce', [T.OpaqueRef, T.Bytes]); export const tx_input_set_entropy = wrap('wally_tx_input_set_entropy', [T.OpaqueRef, T.Bytes]); diff --git a/src/wasm_package/src/index.d.ts b/src/wasm_package/src/index.d.ts index 828ec34c1..f44d718d8 100644 --- a/src/wasm_package/src/index.d.ts +++ b/src/wasm_package/src/index.d.ts @@ -617,6 +617,7 @@ export function tx_get_input_script_len(tx_in: Ref_wally_tx, index: number): num export function tx_get_input_sequence(tx_in: Ref_wally_tx, index: number): number; export function tx_get_input_txhash(tx_in: Ref_wally_tx, index: number): Buffer; export function tx_get_input_witness_len(tx_in: Ref_wally_tx, index: number, wit_index: number): number; +export function tx_get_input_witness_num_items(tx_in: Ref_wally_tx, index: number): number; export function tx_get_length(tx: Ref_wally_tx, flags: number): number; export function tx_get_locktime(tx_in: Ref_wally_tx): number; export function tx_get_num_inputs(tx_in: Ref_wally_tx): number; @@ -648,6 +649,7 @@ export function tx_input_get_script_len(tx_input_in: Ref_wally_tx_input): number export function tx_input_get_sequence(tx_input_in: Ref_wally_tx_input): number; export function tx_input_get_txhash(tx_input_in: Ref_wally_tx_input): Buffer; export function tx_input_get_witness_len(tx_input_in: Ref_wally_tx_input, index: number): number; +export function tx_input_get_witness_num_items(tx_input_in: Ref_wally_tx_input): number; export function tx_input_init(txhash: Buffer|Uint8Array, utxo_index: number, sequence: number, script: Buffer|Uint8Array, witness: Ref_wally_tx_witness_stack): Ref_wally_tx_input; export function tx_input_set_blinding_nonce(tx_input_in: Ref_wally_tx_input, blinding_nonce: Buffer|Uint8Array): void; export function tx_input_set_entropy(tx_input_in: Ref_wally_tx_input, entropy: Buffer|Uint8Array): void; diff --git a/tools/wasm_exports.sh b/tools/wasm_exports.sh index 77f403e6d..dda170db2 100644 --- a/tools/wasm_exports.sh +++ b/tools/wasm_exports.sh @@ -394,6 +394,7 @@ EXPORTED_FUNCTIONS="['_malloc','_free','_bip32_key_free' \ ,'_wally_tx_get_input_txhash' \ ,'_wally_tx_get_input_witness' \ ,'_wally_tx_get_input_witness_len' \ +,'_wally_tx_get_input_witness_num_items' \ ,'_wally_tx_get_length' \ ,'_wally_tx_get_locktime' \ ,'_wally_tx_get_num_inputs' \ @@ -417,6 +418,7 @@ EXPORTED_FUNCTIONS="['_malloc','_free','_bip32_key_free' \ ,'_wally_tx_input_get_txhash' \ ,'_wally_tx_input_get_witness' \ ,'_wally_tx_input_get_witness_len' \ +,'_wally_tx_input_get_witness_num_items' \ ,'_wally_tx_input_init_alloc' \ ,'_wally_tx_input_set_index' \ ,'_wally_tx_input_set_script' \