Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions include/wally_psbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ extern "C" {
#define WALLY_PSBT_TXMOD_SINGLE 0x4 /* SIGHASH_SINGLE signature is present */
#define WALLY_PSET_TXMOD_RESERVED 0x1 /* Elements: Reserved: not used and ignored if set */

#define WALLY_PSBT_PARSE_FLAG_STRICT 0x1 /* Parse strictly according to the PSBT/PSET spec */
#define WALLY_PSBT_PARSE_FLAG_LOOSE 0x2 /* Allow disallowed and missing mandatory fields */
#define WALLY_PSBT_PARSE_FLAG_STRICT 0x1 /* Parse strictly according to the PSBT/PSET spec */
#define WALLY_PSBT_PARSE_FLAG_LOOSE 0x2 /* Allow disallowed and missing mandatory fields */
#define WALLY_PSBT_PARSE_FLAG_COMPLETE 0x4 /* Require parsing to consume the entire input */

/** Include redundant information to match some buggy PSBT implementations */
#define WALLY_PSBT_SERIALIZE_FLAG_REDUNDANT 0x1
Expand Down
1 change: 1 addition & 0 deletions include/wally_psbt_members.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ WALLY_CORE_API int wally_psbt_get_input_utxo_rangeproof_len(const struct wally_p

WALLY_CORE_API int wally_psbt_set_input_amount(struct wally_psbt *psbt, size_t index, uint64_t amount);
WALLY_CORE_API int wally_psbt_clear_input_amount(struct wally_psbt *psbt, size_t index);
WALLY_CORE_API int wally_psbt_has_input_amount(const struct wally_psbt *psbt, size_t index, size_t *written);
WALLY_CORE_API int wally_psbt_set_input_amount_rangeproof(struct wally_psbt *psbt, size_t index, const unsigned char *rangeproof, size_t rangeproof_len);
WALLY_CORE_API int wally_psbt_clear_input_amount_rangeproof(struct wally_psbt *psbt, size_t index);
WALLY_CORE_API int wally_psbt_set_input_asset(struct wally_psbt *psbt, size_t index, const unsigned char *asset, size_t asset_len);
Expand Down
5 changes: 2 additions & 3 deletions src/aes.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,8 @@ int wally_aes(const unsigned char *key, size_t key_len,
AES256_ctx ctx;
size_t written;

if (!are_valid_args(key, key_len, bytes, bytes_len, flags, &written) ||
len % AES_BLOCK_LEN || !bytes_len || bytes_len % AES_BLOCK_LEN ||
!bytes_out || !len)
if (wally_aes_len(key, key_len, bytes, bytes_len, flags, &written) != WALLY_OK ||
!bytes_out || !len || len < bytes_len)
return WALLY_EINVAL;

if (flags & AES_FLAG_ENCRYPT)
Expand Down
6 changes: 6 additions & 0 deletions src/ctest/psbts.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ static const struct psbt_test invalid_psbts[] =
/* PSBT v0 with duplicate 0 length script */
{"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA=", false, true},

/* PSBT v0 with one P2PKH input. Outputs are empty. With trailing byte 0x00 */
{"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAAAA==", false, true},

/* PSBT v2 With Global tx version number < 2 */
{"cHNidP8B+wQCAAAAAQIEAQAAAAEDBAECAwQBBAEAAQUBAAA=", false, true},

Expand All @@ -103,6 +106,9 @@ static const struct psbt_test invalid_psbts[] =
/* PSBT v2 missing tx version */
{"cHNidP8B+wQCAAAAAQQBAAEFAQAA", false, true},

/* PSBT v2 (no inputs, no outputs) with trailing byte 0x00 */
{"cHNidP8BAgQCAAAAAQMEAAAAAAEEAQABBQEAAfsEAgAAAAAA", false, true},

/* PSBT v3 - invalid version number */
{"cHNidP8B+wQDAAAAAQIEAgAAAAEFAQAA", false, true},

Expand Down
8 changes: 8 additions & 0 deletions src/data/psbt.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@
"comment": "PSBT v0 with duplicate 0 length script",
"psbt": "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA="
},
{
"comment": "PSBT v0 with one P2PKH input. Outputs are empty. With trailing byte 0x00",
"psbt": "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAAAA=="
},
{
"comment": "PSBT v2 With Global tx version number < 2",
"psbt": "cHNidP8B+wQCAAAAAQIEAQAAAAEDBAECAwQBBAEAAQUBAAA="
Expand All @@ -128,6 +132,10 @@
"comment": "PSBT v2 missing tx version",
"psbt": "cHNidP8B+wQCAAAAAQQBAAEFAQAA"
},
{
"comment": "PSBT v2 (no inputs, no outputs) with trailing byte 0x00",
"psbt": "cHNidP8BAgQCAAAAAQMEAAAAAAEEAQABBQEAAfsEAgAAAAAA"
},
{
"comment": "PSBT v3 - invalid version number",
"psbt": "cHNidP8B+wQDAAAAAQIEAgAAAAEFAQAA"
Expand Down
51 changes: 47 additions & 4 deletions src/psbt.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
#define PSBT_ID_ALL_FLAGS (WALLY_PSBT_ID_AS_V2 | WALLY_PSBT_ID_USE_LOCKTIME)

/* All allowed flags for wally_psbt_from_[bytes|base64]() */
#define PSBT_ALL_PARSE_FLAGS (WALLY_PSBT_PARSE_FLAG_STRICT|WALLY_PSBT_PARSE_FLAG_LOOSE)
#define PSBT_ALL_PARSE_FLAGS (WALLY_PSBT_PARSE_FLAG_STRICT | \
WALLY_PSBT_PARSE_FLAG_LOOSE | \
WALLY_PSBT_PARSE_FLAG_COMPLETE)

static const uint8_t PSBT_MAGIC[5] = {'p', 's', 'b', 't', 0xff};
static const uint8_t PSET_MAGIC[5] = {'p', 's', 'e', 't', 0xff};
Expand Down Expand Up @@ -2830,6 +2832,8 @@ int wally_psbt_from_bytes(const unsigned char *bytes, size_t len,

if (ret == WALLY_OK && !*cursor)
ret = WALLY_EINVAL; /* Ran out of data */
else if (ret == WALLY_OK && *max && (flags & WALLY_PSBT_PARSE_FLAG_COMPLETE))
ret = WALLY_EINVAL; /* Trailing data */

if (ret != WALLY_OK) {
wally_psbt_free(*output);
Expand Down Expand Up @@ -3523,8 +3527,9 @@ int wally_psbt_from_base64_n(const char *str_in, size_t str_len, uint32_t flags,
goto done;
}

/* decode the psbt */
ret = wally_psbt_from_bytes(decoded, written, flags, output);
/* Parse the psbt. For base64, require all bytes to be consumed. */
ret = wally_psbt_from_bytes(decoded, written,
flags | WALLY_PSBT_PARSE_FLAG_COMPLETE, output);

done:
clear_and_free(decoded, max_len);
Expand Down Expand Up @@ -5932,7 +5937,35 @@ int wally_psbt_clear_input_required_lockheight(struct wally_psbt *psbt, size_t i
PSBT_FIELD(output, taproot_internal_key, PSBT_0)

#ifndef WALLY_ABI_NO_ELEMENTS
#ifndef BUILD_ELEMENTS
PSBT_GET_I_PSET(input, amount, uint64_t, PSBT_2)
int wally_psbt_has_input_amount(const struct wally_psbt *psbt, size_t index, size_t* written)
{
if (written)
*written = 0;
return WALLY_EINVAL;
}
#else
int wally_psbt_get_input_amount(const struct wally_psbt *psbt, size_t index,
uint64_t *written)
{
struct wally_psbt_input *p = psbt_get_input(psbt, index);
if (written) *written = 0;
if (!p || !written || psbt->version != PSBT_2 || !p->has_amount)
return WALLY_EINVAL;
*written = p->amount;
return WALLY_OK;
}
int wally_psbt_has_input_amount(const struct wally_psbt *psbt, size_t index, size_t* written)
{
struct wally_psbt_input *p = psbt_get_input(psbt, index);
if (written) *written = 0;
if (!p || !written || psbt->version != PSBT_2)
return WALLY_EINVAL;
*written = p->has_amount ? 1 : 0;
return WALLY_OK;
}
#endif
int wally_psbt_clear_input_amount(struct wally_psbt *psbt, size_t index) {
if (!psbt || psbt->version != PSBT_2) return WALLY_EINVAL;
return wally_psbt_input_clear_amount(psbt_get_input(psbt, index));
Expand Down Expand Up @@ -5994,7 +6027,17 @@ PSBT_FIELD(output, redeem_script, PSBT_0)
PSBT_FIELD(output, witness_script, PSBT_0)
PSBT_GET_M(output, keypath)
PSBT_GET_M(output, unknown)
PSBT_GET_I(output, amount, uint64_t, PSBT_2)
int wally_psbt_get_output_amount(const struct wally_psbt *psbt, size_t index,
uint64_t *written)
{
struct wally_psbt_output *p = psbt_get_output(psbt, index);
if (written) *written = 0;
if (!p || !written || psbt->version != PSBT_2 || !p->has_amount)
return WALLY_EINVAL;
*written = p->amount;
return WALLY_OK;
}

int wally_psbt_has_output_amount(const struct wally_psbt *psbt, size_t index, size_t *written) {
struct wally_psbt_output *p = psbt_get_output(psbt, index);
if (written) *written = 0;
Expand Down
4 changes: 3 additions & 1 deletion src/swig_java/jni_extra.java_in
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
}

public final static byte[] aes(byte[] jarg1, byte[] jarg2, long jarg3) {
return aes(jarg1, jarg2, jarg3, null);
final int len = aes_len(jarg1, jarg2, jarg3);
final byte[] ret = new byte[len];
return aes(jarg1, jarg2, jarg3, ret);
}

public final static String base58check_from_bytes(byte[] bytes) {
Expand Down
7 changes: 2 additions & 5 deletions src/swig_java/swig.i
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,6 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
%returns_void__(wally_psbt_clear_input_required_locktime);
%returns_void__(wally_psbt_clear_input_sequence);
%returns_void__(wally_psbt_clear_input_utxo_rangeproof);
%returns_void__(wally_psbt_clear_input_value);
%returns_void__(wally_psbt_clear_output_amount);
%returns_void__(wally_psbt_clear_output_asset);
%returns_void__(wally_psbt_clear_output_asset_blinding_surjectionproof);
Expand Down Expand Up @@ -835,7 +834,6 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
%rename("psbt_get_input_utxo") wally_psbt_get_input_utxo_alloc;
%returns_size_t(wally_psbt_get_input_utxo_rangeproof);
%returns_size_t(wally_psbt_get_input_utxo_rangeproof_len);
%returns_uint64(wally_psbt_get_input_value);
%returns_size_t(wally_psbt_get_input_witness_script);
%returns_size_t(wally_psbt_get_input_witness_script_len);
%returns_struct(wally_psbt_get_input_witness_utxo_alloc, wally_tx_output);
Expand All @@ -845,7 +843,7 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
%returns_size_t(wally_psbt_get_locktime);
%returns_size_t(wally_psbt_get_num_inputs);
%returns_size_t(wally_psbt_get_num_outputs);
%returns_size_t(wally_psbt_get_output_amount);
%returns_uint64(wally_psbt_get_output_amount);
%returns_size_t(wally_psbt_get_output_asset);
%returns_size_t(wally_psbt_get_output_asset_len);
%returns_size_t(wally_psbt_get_output_asset_blinding_surjectionproof);
Expand Down Expand Up @@ -887,7 +885,7 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
%returns_size_t(wally_psbt_has_global_genesis_blockhash);
%returns_size_t(wally_psbt_has_input_required_lockheight);
%returns_size_t(wally_psbt_has_input_required_locktime);
%returns_size_t(wally_psbt_has_input_value);
%returns_size_t(wally_psbt_has_input_amount);
%returns_size_t(wally_psbt_has_output_amount);
%returns_size_t(wally_psbt_has_output_asset);
%returns_size_t(wally_psbt_has_output_asset_blinding_surjectionproof);
Expand Down Expand Up @@ -945,7 +943,6 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) {
%returns_void__(wally_psbt_set_input_unknowns);
%returns_void__(wally_psbt_set_input_utxo);
%returns_void__(wally_psbt_set_input_utxo_rangeproof);
%returns_void__(wally_psbt_set_input_value);
%returns_void__(wally_psbt_set_input_witness_script);
%returns_void__(wally_psbt_set_input_witness_utxo);
%returns_void__(wally_psbt_set_input_witness_utxo_from_tx);
Expand Down
50 changes: 30 additions & 20 deletions src/swig_python/contrib/psbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,22 @@ def _try_set(self, func, psbt, valid_value, null_value=None, mandatory=False, al
self._throws(func, psbt, 0, null_value)
self._try_invalid(func, psbt, valid_value)

def _try_get_set_i(self, setfn, clearfn, getfn, psbt, valid_value, invalid_value=None, mandatory=False):
def _try_get_set_i(self, setfn, clearfn, getfn, hasfn, psbt,
valid_value, invalid_value=None, mandatory=False):
self._try_invalid(setfn, psbt, valid_value)
setfn(psbt, 0, valid_value) # Set
if hasfn:
self.assertTrue(hasfn(psbt, 0))
self._round_trip(psbt)
self._try_invalid(getfn, psbt)
ret = getfn(psbt, 0) # Get
self.assertEqual(valid_value, ret)
if clearfn:
self._try_invalid(clearfn, psbt)
clearfn(psbt, 0)
if hasfn:
self.assertFalse(hasfn(psbt, 0))
self._throws(getfn, psbt, 0)
if mandatory:
setfn(psbt, 0, valid_value) # Set Again
else:
Expand Down Expand Up @@ -195,7 +201,7 @@ def test_add_remove_tx_items(self):
# txout has blinded value/asset, expect no values
# and the commitments set in the PSET
self.assertEqual(psbt_has_output_amount(pset2, 0), 0)
self.assertEqual(psbt_get_output_amount(pset2, 0), 0)
self._throws(psbt_get_output_amount, pset2, 0)
self.assertEqual(psbt_get_output_value_commitment_len(pset2, 0), len(blinded_value))
self.assertEqual(psbt_get_output_value_commitment(pset2, 0), blinded_value)
self.assertEqual(psbt_get_output_script(pset2, 0), script)
Expand Down Expand Up @@ -504,7 +510,7 @@ def test_psbt(self):
p, dummy_unknowns, dummy_unknown_key)
psbt_set_input_signatures(p, 0, empty_signatures)
self._try_get_set_i(psbt_set_input_sighash, None,
psbt_get_input_sighash, p, 0xff) # FIXME 0x100 as invalid_value should fail
psbt_get_input_sighash, None, p, 0xff) # FIXME 0x100 as invalid_value should fail
for sig_type in [dummy_sig_tap_default, dummy_sig_tap_all, dummy_sig_tap_single]:
psbt_set_input_taproot_signature(p, 0, sig_type)
self.assertEqual(psbt_get_input_taproot_signature(p, 0), sig_type)
Expand Down Expand Up @@ -542,19 +548,19 @@ def test_psbt(self):

# V2: Output Index
self._throws(psbt_set_input_output_index, psbt, 0, 1234) # Non v2 PSBT
self._try_get_set_i(psbt_set_input_output_index,
None,
psbt_get_input_output_index, psbt2, 1234)
self._try_get_set_i(psbt_set_input_output_index, None,
psbt_get_input_output_index, None,
psbt2, 1234)
# For v0 PSBTs, fetching returns the value from the global tx
out_idx = tx_get_input_index(global_tx, 0)
self.assertEqual(psbt_get_input_output_index(psbt, 0), out_idx)

# V2: Sequence
self._throws(psbt_set_input_sequence, psbt, 0, 1234) # Non v2 PSBT
self._throws(psbt_clear_input_sequence, psbt, 0) # Non v2 PSBT
self._try_get_set_i(psbt_set_input_sequence,
psbt_clear_input_sequence,
psbt_get_input_sequence, psbt2, 1234)
self._try_get_set_i(psbt_set_input_sequence, psbt_clear_input_sequence,
psbt_get_input_sequence, None,
psbt2, 1234)
# If no sequence is present, it defaults to final (0xffffffff)
psbt_clear_input_sequence(psbt2, 0)
self.assertEqual(psbt_get_input_sequence(psbt2, 0), 0xffffffff)
Expand All @@ -573,21 +579,25 @@ def test_psbt(self):
self._throws(g_fn, psbt, 0) # Non v2 PSBT
self._throws(h_fn, psbt, 0) # Non v2 PSBT
self._throws(c_fn, psbt, 0) # Non v2 PSBT
self._try_get_set_i(s_fn, c_fn, g_fn, psbt2, v)
self._try_get_set_i(s_fn, c_fn, g_fn, h_fn, psbt2, v)

#
# Inputs: PSET
#
if is_elements_build():
# PSET: Explicit amount/issuance amount/inflation keys/pegin amount
for setfn, getfn in [
(psbt_set_input_amount, psbt_get_input_amount),
(psbt_set_input_issuance_amount, psbt_get_input_issuance_amount),
(psbt_set_input_inflation_keys, psbt_get_input_inflation_keys),
(psbt_set_input_pegin_amount, psbt_get_input_pegin_amount)]:
for setfn, clearfn, getfn, hasfn in [
(psbt_set_input_amount, psbt_clear_input_amount,
psbt_get_input_amount, psbt_has_input_amount),
(psbt_set_input_issuance_amount, None,
psbt_get_input_issuance_amount, None),
(psbt_set_input_inflation_keys, None,
psbt_get_input_inflation_keys, None),
(psbt_set_input_pegin_amount, None,
psbt_get_input_pegin_amount, None)]:
self._throws(setfn, psbt, 0, 1234) # Non v2 PSBT
self._throws(getfn, psbt, 0) # Non v2 PSBT
self._try_get_set_i(setfn, None, getfn, pset2, 1234)
self._try_get_set_i(setfn, clearfn, getfn, hasfn, pset2, 1234)

# Explicit amount
self._throws(psbt_clear_input_amount, psbt, 0) # Non v2 PSBT
Expand Down Expand Up @@ -687,9 +697,9 @@ def test_psbt(self):
self._throws(psbt_has_output_amount, psbt2, 1) # Invalid Index
self.assertEqual(psbt_has_output_amount(psbt2, 0), 1) # Non v2 PSBT
self._throws(psbt_clear_output_amount, psbt, 0) # Non v2 PSBT
self._try_get_set_i(psbt_set_output_amount,
psbt_clear_output_amount,
psbt_get_output_amount, psbt2, 1234, mandatory=True)
self._try_get_set_i(psbt_set_output_amount, psbt_clear_output_amount,
psbt_get_output_amount, psbt_has_output_amount,
psbt2, 1234, mandatory=True)

# V2: Script
self._throws(psbt_set_output_script, psbt, 0, dummy_bytes) # Non v2 PSBT
Expand All @@ -711,7 +721,7 @@ def test_psbt(self):
(psbt_set_output_blinder_index, psbt_get_output_blinder_index)]:
self._throws(setfn, psbt, 0, 1234) # Non v2 PSBT
self._throws(getfn, psbt, 0) # Non v2 PSBT
self._try_get_set_i(setfn, None, getfn, pset2, 1234)
self._try_get_set_i(setfn, None, getfn, None, pset2, 1234)

cases = [
('value_commitment', dummy_blind_value, dummy_blind_asset),
Expand Down
Loading
Loading