diff --git a/.gitignore b/.gitignore index e5f983e03..79e654972 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ src/test_bech32* src/test_blech32* src/test_clear* src/test_tx* +src/test_psbt* src/test_elements_tx* src/test-suite.log src/swig_java/swig_java_wrap.c @@ -86,6 +87,9 @@ docs/source/transaction.rst docs/source/elements.rst .idea/ +# ctags +tags + # MacOS **/.DS_Store diff --git a/configure.ac b/configure.ac index 8fb802f5c..40cca2092 100644 --- a/configure.ac +++ b/configure.ac @@ -174,6 +174,9 @@ AX_CHECK_COMPILE_FLAG([-Wpedantic], [AM_CFLAGS="-Wpedantic $AM_CFLAGS"]) AX_CHECK_COMPILE_FLAG([-Wextra], [AM_CFLAGS="-Wextra $AM_CFLAGS"]) AX_CHECK_COMPILE_FLAG([-Wall], [AM_CFLAGS="-Wall $AM_CFLAGS"]) +# Needed for Ubuntu 20.04LTS when compiling as a library +AX_CHECK_COMPILE_FLAG([-D_DEFAULT_SOURCE=1], [AM_CFLAGS="-D_DEFAULT_SOURCE=1 $AM_CFLAGS"]) + # Disable some unhelpful errors from those enabled above AX_CHECK_COMPILE_FLAG([-Wno-unused-function], [AM_CFLAGS="$AM_CFLAGS -Wno-unused-function"]) AX_CHECK_COMPILE_FLAG([-Wno-long-long], [AM_CFLAGS="$AM_CFLAGS -Wno-long-long"]) diff --git a/include/wally_psbt.h b/include/wally_psbt.h index 1743c72f3..30a13222d 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -3,6 +3,7 @@ #include "wally_transaction.h" #include "wally_core.h" +#include #ifdef __cplusplus extern "C" { @@ -10,7 +11,11 @@ extern "C" { #define WALLY_PSBT_SEPARATOR 0x00 +/* The proprietary type is the same for all scopes */ +#define WALLY_PSBT_PROPRIETARY_TYPE 0xFC + #define WALLY_PSBT_GLOBAL_UNSIGNED_TX 0x00 +#define WALLY_PSBT_GLOBAL_VERSION 0xFB #define WALLY_PSBT_IN_NON_WITNESS_UTXO 0x00 #define WALLY_PSBT_IN_WITNESS_UTXO 0x01 @@ -22,10 +27,35 @@ extern "C" { #define WALLY_PSBT_IN_FINAL_SCRIPTSIG 0x07 #define WALLY_PSBT_IN_FINAL_SCRIPTWITNESS 0x08 +#ifdef BUILD_ELEMENTS +#define WALLY_PSBT_IN_ELEMENTS_VALUE 0x00 +#define WALLY_PSBT_IN_ELEMENTS_VALUE_BLINDER 0x01 +#define WALLY_PSBT_IN_ELEMENTS_ASSET 0x02 +#define WALLY_PSBT_IN_ELEMENTS_ASSET_BLINDER 0x03 +#define WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX 0x04 +#define WALLY_PSBT_IN_ELEMENTS_TXOUT_PROOF 0x05 +#define WALLY_PSBT_IN_ELEMENTS_GENESIS_HASH 0x06 +#define WALLY_PSBT_IN_ELEMENTS_CLAIM_SCRIPT 0x07 +#endif /* BUILD ELEMENTS */ + #define WALLY_PSBT_OUT_REDEEM_SCRIPT 0x00 #define WALLY_PSBT_OUT_WITNESS_SCRIPT 0x01 #define WALLY_PSBT_OUT_BIP32_DERIVATION 0x02 +#ifdef BUILD_ELEMENTS +#define WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT 0x00 +#define WALLY_PSBT_OUT_ELEMENTS_VALUE_BLINDER 0x01 +#define WALLY_PSBT_OUT_ELEMENTS_ASSET_COMMITMENT 0x02 +#define WALLY_PSBT_OUT_ELEMENTS_ASSET_BLINDER 0x03 +#define WALLY_PSBT_OUT_ELEMENTS_RANGE_PROOF 0x04 +#define WALLY_PSBT_OUT_ELEMENTS_SURJECTION_PROOF 0x05 +#define WALLY_PSBT_OUT_ELEMENTS_BLINDING_PUBKEY 0x06 +#define WALLY_PSBT_OUT_ELEMENTS_NONCE_COMMITMENT 0x07 +#endif /* BUILD ELEMENTS */ + +/* PSBT Version number */ +#define WALLY_PSBT_HIGHEST_VERSION 0 + #ifdef SWIG struct wally_key_origin_info; struct wally_keypath_map; @@ -100,6 +130,24 @@ struct wally_psbt_input { struct wally_partial_sigs_map *partial_sigs; struct wally_unknowns_map *unknowns; uint32_t sighash_type; + +#ifdef BUILD_ELEMENTS + uint64_t value; + bool has_value; + unsigned char *value_blinder; + size_t value_blinder_len; + unsigned char *asset; + size_t asset_len; + unsigned char *asset_blinder; + size_t asset_blinder_len; + struct wally_tx *peg_in_tx; + unsigned char *txout_proof; + size_t txout_proof_len; + unsigned char *genesis_hash; + size_t genesis_hash_len; + unsigned char *claim_script; + size_t claim_script_len; +#endif /* BUILD_ELEMENTS */ }; /** A psbt output map */ @@ -110,10 +158,29 @@ struct wally_psbt_output { size_t witness_script_len; struct wally_keypath_map *keypaths; struct wally_unknowns_map *unknowns; +#ifdef BUILD_ELEMENTS + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]; + bool has_blinding_pubkey; + unsigned char *value_commitment; + size_t value_commitment_len; + unsigned char *value_blinder; + size_t value_blinder_len; + unsigned char *asset_commitment; + size_t asset_commitment_len; + unsigned char *asset_blinder; + size_t asset_blinder_len; + unsigned char *nonce_commitment; + size_t nonce_commitment_len; + unsigned char *range_proof; + size_t range_proof_len; + unsigned char *surjection_proof; + size_t surjection_proof_len; +#endif /* BUILD_ELEMENTS */ }; /** A partially signed bitcoin transaction */ struct wally_psbt { + unsigned char magic[5]; struct wally_tx *tx; struct wally_psbt_input *inputs; size_t num_inputs; @@ -122,6 +189,7 @@ struct wally_psbt { size_t num_outputs; size_t outputs_allocation_len; struct wally_unknowns_map *unknowns; + uint32_t version; }; #endif /* SWIG */ @@ -509,7 +577,10 @@ WALLY_CORE_API int wally_psbt_get_length( * :param psbt: the PSBT to serialize. * :param bytes_out: Bytes to create the transaction from. * :param bytes_len: Length of ``bytes`` in bytes (use `wally_psbt_get_length`). - * :param bytes_written: number of bytes written to bytes_out + * :param bytes_written: number of bytes written to bytes_out. + * + * If @bytes_len is insufficient, this will return WALLY_EINVAL, but + * @bytes_written will be filled in the the amount which would be required. */ WALLY_CORE_API int wally_psbt_to_bytes( const struct wally_psbt *psbt, @@ -578,6 +649,333 @@ WALLY_CORE_API int wally_extract_psbt( struct wally_psbt *psbt, struct wally_tx **output); +/** + * Determine if a psbt is an elements psbt. + * + * :param psbt: The psbt to check. + * :param written: 1 if the transaction is an elements psbt, otherwise 0. + */ +WALLY_CORE_API int wally_psbt_is_elements( + const struct wally_psbt *psbt, + size_t *written); + +#ifdef BUILD_ELEMENTS +/** + * Allocate and initialize a new elements psbt. + * + * :param inputs_allocation_len: The number of inputs to pre-allocate space for. + * :param outputs_allocation_len: The number of outputs to pre-allocate space for. + * :param global_unknowns_allocation_len: The number of global unknowns to allocate space for. + * :param output: Destination for the resulting psbt output. + */ +WALLY_CORE_API int wally_psbt_elements_init_alloc( + size_t inputs_allocation_len, + size_t outputs_allocation_len, + size_t global_unknowns_allocation_len, + struct wally_psbt **output); + +/** + * Allocate and initialize a new psbt elements input. + * + * :param non_witness_utxo: The non witness utxo for this input if it exists. + * :param witness_utxo: The witness utxo for this input if it exists. + * :param redeem_script: The redeem script for this input + * :param redeem_script_len: The length of the redeem script. + * :param witness_script: The witness script for this input + * :param witness_script_len: The length of the witness script. + * :param final_script_sig: The scriptSig for this input + * :param final_script_sig_len: Size of ``final_script_sig`` in bytes. + * :param final_witness: The witness stack for the input, or NULL if no witness is present. + * :param keypaths: The HD keypaths for this input. + * :param partial_sigs: The partial signatures for this input. + * :param unknowns: The unknown key value pairs for this input. + * :param sighash_type: The sighash type for this input + * :param value: The value for this input + * :param value_blinder: The value blinder for ths input + * :param value_blinder_len: The length of the value_blinder. + * :param asset: The witness script for this input + * :param asset_len: The length of the witness script. + * :param asset_blinder: The witness script for this input + * :param asset_blinder__len: The length of the witness script. + * :param peg_in_tx: The witness script for this input + * :param txout_proof: The witness script for this input + * :param txout_proof_len: The length of the witness script. + * :param genesis_hash: The witness script for this input + * :param genesis_hash_len: The length of the witness script. + * :param claim_script: The witness script for this input + * :param claim_script_len: The length of the witness script. + * :param output: Destination for the resulting psbt input. + */ +WALLY_CORE_API int wally_psbt_elements_input_init_alloc( + struct wally_tx *non_witness_utxo, + struct wally_tx_output *witness_utxo, + unsigned char *redeem_script, + size_t redeem_script_len, + unsigned char *witness_script, + size_t witness_script_len, + unsigned char *final_script_sig, + size_t final_script_sig_len, + struct wally_tx_witness_stack *final_witness, + struct wally_keypath_map *keypaths, + struct wally_partial_sigs_map *partial_sigs, + struct wally_unknowns_map *unknowns, + uint32_t sighash_type, + uint64_t value, + bool has_value, + unsigned char *value_blinder, + size_t value_blinder_len, + unsigned char *asset, + size_t asset_len, + unsigned char *asset_blinder, + size_t asset_blinder_len, + struct wally_tx *peg_in_tx, + unsigned char *txout_proof, + size_t txout_proof_len, + unsigned char *genesis_hash, + size_t genesis_hash_len, + unsigned char *claim_script, + size_t claim_script_len, + struct wally_psbt_input **output); + +/** + * Set the value in an elements input + * + * :param input: The input to update. + * :param value: The value for this input + */ +WALLY_CORE_API int wally_psbt_elements_input_set_value( + struct wally_psbt_input *input, + uint64_t value); + +/** + * Set the value blinder in an elements input + * + * :param input: The input to update. + * :param value_blinder: The value blinder for this input + * :param value_blinder_len: The length of the value blinder. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_value_blinder( + struct wally_psbt_input *input, + unsigned char *value_blinder, + size_t value_blinder_len); + +/** + * Set the asset in an elements input + * + * :param input: The input to update. + * :param asset: The asset for this input + * :param asset_len: The length of the asset + */ +WALLY_CORE_API int wally_psbt_elements_input_set_asset( + struct wally_psbt_input *input, + unsigned char *asset, + size_t asset_len); + +/** + * Set the asset blinder in an elements input + * + * :param input: The input to update. + * :param asset_blinder: The asset blinder for this input + * :param asset_blinder_len: The length of the asset blinder. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_asset_blinder( + struct wally_psbt_input *input, + unsigned char *asset_blinder, + size_t asset_blinder_len); + +/** + * Set the peg in tx in an input + * + * :param input: The input to update. + * :param peg_in_tx: The peg in tx for this input if it exists. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_peg_in_tx( + struct wally_psbt_input *input, + struct wally_tx *peg_in_tx); + +/** + * Set the txout proof in an elements input + * + * :param input: The input to update. + * :param txout_proof: The txout proof for this input + * :param txout_proof_len: The length of the txout proof. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_txout_proof( + struct wally_psbt_input *input, + unsigned char *txout_proof, + size_t txout_proof_len); + +/** + * Set the genesis hash in an elements input + * + * :param input: The input to update. + * :param genesis_hash: The genesis hash for this input + * :param genesis_hash_len: The length of the genesis hash. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_genesis_hash( + struct wally_psbt_input *input, + unsigned char *genesis_hash, + size_t genesis_hash_len); + +/** + * Set the claim script in an elements input + * + * :param input: The input to update. + * :param claim_script: The claim script for this input + * :param claim_script_len: The length of the claim_script. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_claim_script( + struct wally_psbt_input *input, + unsigned char *claim_script, + size_t claim_script_len); + +/** + * Allocate and initialize a new psbt elements output. + * + * :param redeem_script: The redeem script needed for spending this output + * :param redeem_script_len: The length of the redeem script. + * :param witness_script: The witness script needed for spending for this output + * :param witness_script_len: The length of the witness script. + * :param keypaths: The HD keypaths for the keys needed for spending this output + * :param unknowns: The unknown key value pairs for this output. + * :param blinding_pubkey: The blinding pubkey for this output + * :param value_commitment: The value commitment for this output + * :param value_commitment_len: The length of the witness script. + * :param value_blinder: The value blinder for this output + * :param value_blinder_len: The length of the value blinder. + * :param asset_commitment: The asset commitment for this output + * :param asset_commitment_len: The length of the asset commitment. + * :param asset_blinder: The asset blinder for this output + * :param asset_blinder_len: The length of the asset blinder. + * :param nonce_commitment: The nonce commitment for this output + * :param nonce_commitment_len: The length of the nonce commitment. + * :param range_proof: The range proof for this output + * :param range_proof_len: The length of the range proof. + * :param surjection_proof: The surjection proof for this output + * :param surjection_proof_len: The length of the surjection proof. + * :param output: Destination for the resulting psbt output. + */ +WALLY_CORE_API int wally_psbt_elements_output_init_alloc( + unsigned char *redeem_script, + size_t redeem_script_len, + unsigned char *witness_script, + size_t witness_script_len, + struct wally_keypath_map *keypaths, + struct wally_unknowns_map *unknowns, + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN], + bool has_blinding_pubkey, + unsigned char *value_commitment, + size_t value_commitment_len, + unsigned char *value_blinder, + size_t value_blinder_len, + unsigned char *asset_commitment, + size_t asset_commitment_len, + unsigned char *asset_blinder, + size_t asset_blinder_len, + unsigned char *nonce_commitment, + size_t nonce_commitment_len, + unsigned char *range_proof, + size_t range_proof_len, + unsigned char *surjection_proof, + size_t surjection_proof_len, + struct wally_psbt_output **output); + +/** + * Set the blinding pubkey in an elements output + * + * :param output: The output to update. + * :param blinding_pubkey: The blinding pubkey for this output + */ +WALLY_CORE_API int wally_psbt_elements_output_set_blinding_pubkey( + struct wally_psbt_output *output, + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]); + +/** + * Set the value commitment in an elements output + * + * :param output: The output to update. + * :param value_commitment: The value commitment for this output + * :param value_commitment_len: The length of the value commitment. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_value_commitment( + struct wally_psbt_output *output, + unsigned char *value_commitment, + size_t value_commitment_len); + +/** + * Set the value blinder in an elements output + * + * :param output: The output to update. + * :param value_blinder: The value blinder for this output + * :param value_blinder_len: The length of the value blinder. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_value_blinder( + struct wally_psbt_output *output, + unsigned char *value_blinder, + size_t value_blinder_len); + +/** + * Set the asset commitment in an elements output + * + * :param output: The output to update. + * :param asset_commitment: The asset commitment for this output + * :param asset_commitment_len: The length of the asset commitment. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_asset_commitment( + struct wally_psbt_output *output, + unsigned char *asset_commitment, + size_t asset_commitment_len); + +/** + * Set the asset blinder in an elements output + * + * :param output: The output to update. + * :param asset_blinder: The asset blinder for this output + * :param asset_blinder_len: The length of the asset blinder. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_asset_blinder( + struct wally_psbt_output *output, + unsigned char *asset_blinder, + size_t asset_blinder_len); + +/** + * Set the nonce commitment in an elements output + * + * :param output: The output to update. + * :param nonce_commitment: The nonce commitment for this output + * :param nonce_commitment_len: The length of the nonce commitment. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_nonce_commitment( + struct wally_psbt_output *output, + unsigned char *nonce_commitment, + size_t nonce_commitment_len); + +/** + * Set the range proof in an elements output + * + * :param output: The output to update. + * :param range_proof: The range_proof for this output + * :param range_proof_len: The length of the raange proof. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_range_proof( + struct wally_psbt_output *output, + unsigned char *range_proof, + size_t range_proof_len); + +/** + * Set the surjection proof in an elements output + * + * :param output: The output to update. + * :param surjection_proof: The surjection proof for this output + * :param surjection_proof: The length of the surjection proof. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_surjection_proof( + struct wally_psbt_output *output, + unsigned char *surjection_proof, + size_t surjection_proof_len); + +#endif /* BUILD_ELEMENTS */ + #ifdef __cplusplus } #endif diff --git a/include/wally_transaction.h b/include/wally_transaction.h index 602725867..d7daf6497 100644 --- a/include/wally_transaction.h +++ b/include/wally_transaction.h @@ -396,6 +396,9 @@ WALLY_CORE_API int wally_tx_get_witness_count( WALLY_CORE_API int wally_tx_free(struct wally_tx *tx); #endif /* SWIG_PYTHON */ +WALLY_CORE_API int wally_clone_output_to(struct wally_tx_output *dst, + const struct wally_tx_output *src); + /** * Return the length of transaction once serialized into bytes. * @@ -946,6 +949,7 @@ WALLY_CORE_API int wally_tx_elements_issuance_calculate_reissuance_token( unsigned char *bytes_out, size_t len); + #endif /* BUILD_ELEMENTS */ #ifdef __cplusplus diff --git a/src/Makefile.am b/src/Makefile.am index f69b4cefe..8a29db3f8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -185,6 +185,7 @@ libwallycore_la_SOURCES = \ mnemonic.c \ pbkdf2.c \ psbt.c \ + pullpush.c \ script.c \ scrypt.c \ sign.c \ @@ -225,6 +226,16 @@ noinst_PROGRAMS += test_bech32 test_bech32_SOURCES = ctest/test_bech32.c test_bech32_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS) test_bech32_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@ +TESTS += test_psbt +noinst_PROGRAMS += test_psbt +test_psbt_SOURCES = ctest/test_psbt.c ccan/ccan/str/hex/hex.c +test_psbt_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS) -I$(srcdir)/ccan +test_psbt_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@ +TESTS += test_psbt_limits +noinst_PROGRAMS += test_psbt_limits +test_psbt_limits_SOURCES = ctest/test_psbt_limits.c ccan/ccan/str/hex/hex.c +test_psbt_limits_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS) -I$(srcdir)/ccan +test_psbt_limits_LDADD = $(lib_LTLIBRARIES) @CTEST_EXTRA_STATIC@ if USE_PTHREAD TESTS += test_clear noinst_PROGRAMS += test_clear @@ -275,6 +286,9 @@ if RUN_PYTHON_TESTS $(AM_V_at)$(PYTHON_TEST) test/test_transaction.py $(AM_V_at)$(PYTHON_TEST) test/test_wif.py $(AM_V_at)$(PYTHON_TEST) test/test_wordlist.py +if BUILD_ELEMENTS + $(AM_V_at)$(PYTHON_TEST) test/test_pset.py +endif if USE_SWIG_PYTHON $(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/address.py $(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/bip32.py diff --git a/src/ctest/psbts.h b/src/ctest/psbts.h new file mode 100644 index 000000000..375223386 --- /dev/null +++ b/src/ctest/psbts.h @@ -0,0 +1,320 @@ +struct psbt_test { + const char *hex; + const char *base64; +}; + +/* From BIP 174: + * + * The following are invalid PSBTs: + */ +static const struct psbt_test invalid_psbts[] = { +/* + Case: Network transaction, not PSBT format + Bytes in Hex: +*/ + { "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300", + +/* + Base64 String: +*/ + "AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAABqRzBEAiBwsiRRI+a/R01gxbUMBD1MaRpdJDXwmjSnZiqdwlF5CgIgATKcqdrPKAvfMHQOwDkEIkIsgctFg5RXrrdvwS7dlbMBIQJlfRGNM1e44PTCzUbbezn22cONmnCry5st5dyNv+TOMf7///8C09/1BQAAAAAZdqkU0MWZA8W6woaHYOkP1SGkZlqnZSCIrADh9QUAAAAAF6kUNUXm4zuDLEcFDyTT7rk8nAOUi8eHsy4TAA=="}, + +/* + Case: PSBT missing outputs + Bytes in Hex: +*/ + { "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000", +/* + Base64 String: +*/ + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA=="}, + +/* + Case: PSBT where one input has a filled scriptSig in the unsigned tx + Bytes in Hex: + +*/ + { "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000", +/* + Base64 String: +*/ + "cHNidP8BAP0KAQIAAAACqwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QAAAAAakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpL+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAABASAA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHhwEEFgAUhdE1N/LiZUBaNNuvqePdoB+4IwgAAAA="}, + +/* + Case: PSBT where inputs and outputs are provided but without an unsigned tx + Bytes in Hex: + +*/ + { "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000", +/* + Base64 String: +*/ + "cHNidP8AAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAA=="}, + +/* + Case: PSBT with duplicate keys in an input + Bytes in Hex: + +*/ + { "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000", +/* + Base64 String: +*/ + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQA/AgAAAAH//////////////////////////////////////////wAAAAAA/////wEAAAAAAAAAAANqAQAAAAAAAAAA"}, + +/* + Case: PSBT With invalid global transaction typed key + Bytes in Hex: + +*/ + { "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", +/* + Base64 String: +*/ + "cHNidP8CAAFVAgAAAAEnmiMjpd+1H8RfIg+liw/BPh4zQnkqhdfjbNYzO1y8OQAAAAAA/////wGgWuoLAAAAABl2qRT/6cAGEJfMO2NvLLBGD6T8Qn0rRYisAAAAAAABASCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"}, + +/* + Case: PSBT With invalid input witness utxo typed key + Bytes in Hex: + +*/ + { "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", +/* + Base64 String: +*/ + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAIBACCVXuoLAAAAABepFGNFIA9o0YnhrcDfHE0W6o8UwNvrhyICA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GRjBDAiAEJLWO/6qmlOFVnqXJO7/UqJBkIkBVzfBwtncUaUQtBwIfXI6w/qZRbWC4rLM61k7eYOh4W/s6qUuZvfhhUduamgEBBCIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"}, + +/* + Case: PSBT With invalid pubkey length for input partial signature typed key + Bytes in Hex: + +*/ + { "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", +/* + Base64 String: +*/ + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIQIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYwQwIgBCS1jv+qppThVZ6lyTu/1KiQZCJAVc3wcLZ3FGlELQcCH1yOsP6mUW1guKyzOtZO3mDoeFv7OqlLmb34YVHbmpoBAQQiACB3H9GK1FlmbdSfPVZOPbxC9MhHdONgraFoFqjtSI1WgQEFR1IhA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GIQPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvVKuIgYDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYQtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA=="}, + +/* + Case: PSBT With invalid redeemscript typed key + Bytes in Hex: + +*/ + { "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", +/* + Base64 String: +*/ + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQIEACIAIHcf0YrUWWZt1J89Vk49vEL0yEd042CtoWgWqO1IjVaBAQVHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"}, + +/* + Case: PSBT With invalid witnessscript typed key + Bytes in Hex: + +*/ + { "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", +/* + Base64 String: +*/ + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoECBQBHUiEDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUYhA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9Uq4iBgOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RhC0prpnAAAAgAAAAIAEAACAIgYD3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg70QtKa6ZwAAAIAAAACABQAAgAAA"}, + +/* + Case: PSBT With invalid bip32 typed key + Bytes in Hex: + +*/ + { "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", +/* + Base64 String: +*/ + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriEGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb0QtKa6ZwAAAIAAAACABAAAgCIGA95V0eHayAXj+KWMH7+blMAvPbqv4Sf+/KSZXyb4IIO9ELSmumcAAACAAAAAgAUAAIAAAA=="}, + +/* + Case: PSBT With invalid non-witness utxo typed key + Bytes in Hex: + +*/ + { "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", +/* + Base64 String: +*/ + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAIAALsCAAAAAarXOTEBi9JfhK5AC2iEi+CdtwbqwqwYKYur7nGrZW+LAAAAAEhHMEQCIFj2/HxqM+GzFUjUgcgmwBW9MBNarULNZ3kNq2bSrSQ7AiBKHO0mBMZzW2OT5bQWkd14sA8MWUL7n3UYVvqpOBV9ugH+////AoDw+gIAAAAAF6kUD7lGNCFpa4LIM68kHHjBfdveSTSH0PIKJwEAAAAXqRQpynT4oI+BmZQoGFyXtdhS5AY/YYdlAAAAAQfaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="}, + +/* + Case: PSBT With invalid final scriptsig typed key + Bytes in Hex: + +*/ + { "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", +/* + Base64 String: +*/ + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAACBwDaAEcwRAIgdAGK1BgAl7hzMjwAFXILNoTMgSOJEEjn282bVa1nnJkCIHPTabdA4+tT3O+jOCPIBwUUylWn3ZVE8VfBZ5EyYRGMAUgwRQIhAPYQOLMI3B2oZaNIUnRvAVdyk0IIxtJEVDk82ZvfIhd3AiAFbmdaZ1ptCgK4WxTl4pB02KJam1dgvqKBb2YZEKAG6gFHUiEClYO/Oa4KYJdHrRma3dY0+mEIVZ1sXNObTCGD8auW4H8hAtq2H/SaFNtqfQKwzR+7ePxLGDErW05U2uTbovv+9TbXUq4AAQEgAMLrCwAAAAAXqRS39fr0Dj1ApaRZsds1NfK3L6kh6IcBByMiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEI2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="}, + +/* + Case: PSBT With invalid final script witness typed key + Bytes in Hex: + +*/ + { "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", +/* + Base64 String: +*/ + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAggA2gQARzBEAiBi63pVYQenxz9FrEq1od3fb3B1+xJ1lpp/OD7/94S8sgIgDAXbt0cNvy8IVX3TVscyXB7TCRPpls04QJRdsSIo2l8BRzBEAiBl9FulmYtZon/+GnvtAWrx8fkNVLOqj3RQql9WolEDvQIgf3JHA60e25ZoCyhLVtT/y4j3+3Weq74IqjDym4UTg9IBR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuACICA6mkw39ZltOqJdusa1cK8GUDlEkpQkYLNUdT7Z7spYdxENkMak8AAACAAAAAgAQAAIAAIgICf2OZdX0u/1WhNq0CxoSxg4tlVuXxtrNCgqlLa1AFEJYQ2QxqTwAAAIAAAACABQAAgAA="}, + +/* + Case: PSBT With invalid pubkey in output BIP 32 derivation paths typed key + Bytes in Hex: + +*/ + { "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", +/* + Base64 String: +*/ + "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA"}, + +/* + Case: PSBT With invalid input sighash type typed key + Bytes in Hex: + +*/ + { "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00", +/* + Base64 String: +*/ + "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"}, + +/* + Case: PSBT With invalid output redeemScript typed key + Bytes in Hex: + +*/ + { "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00", +/* + Base64 String: +*/ + "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A"}, + +/* + Case: PSBT With invalid output witnessScript typed key + Bytes in Hex: + +*/ + { "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d06d57f8a8751ae00", +/* + Base64 String: +*/ + "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnQbVf4qHUa4A"}, + +/* The following are a bunch of PSBTs with duplicate 0 length scripts */ + { "70736274ff01002a02000000000140420f000000000017a9146e91b72d5593e7d4391e2ff44e91e985c31641f08700000000000100000100016a00", + "cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQAAAQABagA="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff000000000000010109ffffffffffffffff000101090000cafe000000000000", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJ//////////8AAQEJAADK/gAAAAAAAA=="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff000000000000010304addedfee0103040100000000", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQMErd7f7gEDBAEAAAAA"}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff0000000000000104000104016a00", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQQAAQQBagA="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff00000000000001010900e1f50500000000000105000105015100", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQUAAQUBUQA="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff0000000000000107000107015100", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff00000000000001010900e1f50500000000000108010001080301015100", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA="}, + +/* Unsupported version number */ + { "70736274ff01fb04010000000100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000", + "cHNidP8B+wQBAAAAAQB1AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAAAA/v///wLT3/UFAAAAABl2qRTQxZkDxbrChodg6Q/VIaRmWqdlIIisAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4ezLhMAAAEA/aUBAQAAAAABAomjxx6rTSDgNxu7pMxpj6KVyUY6+i45f4UzzLYvlWflAQAAABcWABS+GNFSqbASA52vPafeT1M0nuy5hf////+G+KpDpx3/FEiJOlMKcjfva0YIu7LdLQFx5jrsakiQtAEAAAAXFgAU/j6e8adF6XTZAsQ1WUOryzS9U1P/////AgDC6wsAAAAAGXapFIXP8Ql/2eAIuzSvcJxiGXs4l4pIiKxy/vhOLAAAABepFDOXJboh79Yqx1OpvNBn1semo50FhwJHMEQCICcSviLgJw85T1aDEdx8qaaJcLgCX907JAIp8H+KXzokAiABizjX3NMU5zTJJ2vW+0D2czJbxLqhRMgA0vLwLbJ2XAEhA9LhVnSUG61KmWNyy4fhhW02UmBtmFYv45xenn5BPyEFAkgwRQIhANErhS2F3Nlh0vX0q2YGVN9u7cx5TAwzzlzDCf+1/OWNAiBnM4qODhclwZf7GoivWfUeROQlWyAWfIaEAxwF0fJZKgEhAiO3K+7wll0Qvgd47+zWH8rG95pOoWk5M4BzRGT4TyqzAAAAAAAAAA=="}, + +}; + +/* The following are valid PSBTs: + */ +static const struct psbt_test valid_psbts[] = { +/* + Case: PSBT with one P2PKH input. Outputs are empty + Bytes in Hex: + +*/ + { "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000", +/* + Base64 String: +*/ + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA"}, + +/* + Case: PSBT with one P2PKH input and one P2SH-P2WPKH input. First input is signed and finalized. Outputs are empty + Bytes in Hex: + +*/ + { "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000", +/* + Base64 String: +*/ + "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA"}, + +/* + Case: PSBT with one P2PKH input which has a non-final scriptSig and has a sighash type specified. Outputs are empty + Bytes in Hex: + +*/ + { "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000", +/* + Base64 String: +*/ + "cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAQMEAQAAAAAAAA=="}, + +/* + Case: PSBT with one P2PKH input and one P2SH-P2WPKH input both with non-final scriptSigs. P2SH-P2WPKH input's redeemScript is available. Outputs filled. + Bytes in Hex: + +*/ + { "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000", +/* + Base64 String: +*/ + "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEA3wIAAAABJoFxNx7f8oXpN63upLN7eAAMBWbLs61kZBcTykIXG/YAAAAAakcwRAIgcLIkUSPmv0dNYMW1DAQ9TGkaXSQ18Jo0p2YqncJReQoCIAEynKnazygL3zB0DsA5BCJCLIHLRYOUV663b8Eu3ZWzASECZX0RjTNXuOD0ws1G23s59tnDjZpwq8ubLeXcjb/kzjH+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA="}, + +/* + Case: PSBT with one P2SH-P2WSH input of a 2-of-2 multisig, redeemScript, witnessScript, and keypaths are available. Contains one signature. + Bytes in Hex: + +*/ + { "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000", +/* + Base64 String: +*/ + "cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIDsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQOxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA="}, + +/* + Case: PSBT with one P2WSH input of a 2-of-2 multisig. witnessScript, keypaths, and global xpubs are available. Contains no signatures. Outputs filled. + Bytes in Hex: + +*/ + { "70736274ff01005202000000019dfc6628c26c5899fe1bd3dc338665bfd55d7ada10f6220973df2d386dec12760100000000ffffffff01f03dcd1d000000001600147b3a00bfdc14d27795c2b74901d09da6ef133579000000004f01043587cf02da3fd0088000000097048b1ad0445b1ec8275517727c87b4e4ebc18a203ffa0f94c01566bd38e9000351b743887ee1d40dc32a6043724f2d6459b3b5a4d73daec8fbae0472f3bc43e20cd90c6a4fae000080000000804f01043587cf02da3fd00880000001b90452427139cd78c2cff2444be353cd58605e3e513285e528b407fae3f6173503d30a5e97c8adbc557dac2ad9a7e39c1722ebac69e668b6f2667cc1d671c83cab0cd90c6a4fae000080010000800001012b0065cd1d000000002200202c5486126c4978079a814e13715d65f36459e4d6ccaded266d0508645bafa6320105475221029da12cdb5b235692b91536afefe5c91c3ab9473d8e43b533836ab456299c88712103372b34234ed7cf9c1fea5d05d441557927be9542b162eb02e1ab2ce80224c00b52ae2206029da12cdb5b235692b91536afefe5c91c3ab9473d8e43b533836ab456299c887110d90c6a4fae0000800000008000000000220603372b34234ed7cf9c1fea5d05d441557927be9542b162eb02e1ab2ce80224c00b10d90c6a4fae0000800100008000000000002202039eff1f547a1d5f92dfa2ba7af6ac971a4bd03ba4a734b03156a256b8ad3a1ef910ede45cc500000080000000800100008000", +/* + Base64 String: +*/ + "cHNidP8BAFICAAAAAZ38ZijCbFiZ/hvT3DOGZb/VXXraEPYiCXPfLTht7BJ2AQAAAAD/////AfA9zR0AAAAAFgAUezoAv9wU0neVwrdJAdCdpu8TNXkAAAAATwEENYfPAto/0AiAAAAAlwSLGtBEWx7IJ1UXcnyHtOTrwYogP/oPlMAVZr046QADUbdDiH7h1A3DKmBDck8tZFmztaTXPa7I+64EcvO8Q+IM2QxqT64AAIAAAACATwEENYfPAto/0AiAAAABuQRSQnE5zXjCz/JES+NTzVhgXj5RMoXlKLQH+uP2FzUD0wpel8itvFV9rCrZp+OcFyLrrGnmaLbyZnzB1nHIPKsM2QxqT64AAIABAACAAAEBKwBlzR0AAAAAIgAgLFSGEmxJeAeagU4TcV1l82RZ5NbMre0mbQUIZFuvpjIBBUdSIQKdoSzbWyNWkrkVNq/v5ckcOrlHPY5DtTODarRWKZyIcSEDNys0I07Xz5wf6l0F1EFVeSe+lUKxYusC4ass6AIkwAtSriIGAp2hLNtbI1aSuRU2r+/lyRw6uUc9jkO1M4NqtFYpnIhxENkMak+uAACAAAAAgAAAAAAiBgM3KzQjTtfPnB/qXQXUQVV5J76VQrFi6wLhqyzoAiTACxDZDGpPrgAAgAEAAIAAAAAAACICA57/H1R6HV+S36K6evaslxpL0DukpzSwMVaiVritOh75EO3kXMUAAACAAAAAgAEAAIAA"}, + +/* + Case: PSBT with unknown types in the inputs. + Bytes in Hex: + +*/ + { "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000", +/* + Base64 String: +*/ + "cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA="}, + +/* + Case: PSBT with `PSBT_GLOBAL_XPUB`. + Bytes in Hex: + +*/ + { "70736274ff01009d0100000002710ea76ab45c5cb6438e607e59cc037626981805ae9e0dfd9089012abb0be5350100000000ffffffff190994d6a8b3c8c82ccbcfb2fba4106aa06639b872a8d447465c0d42588d6d670000000000ffffffff0200e1f505000000001976a914b6bc2c0ee5655a843d79afedd0ccc3f7dd64340988ac605af405000000001600141188ef8e4ce0449eaac8fb141cbf5a1176e6a088000000004f010488b21e039e530cac800000003dbc8a5c9769f031b17e77fea1518603221a18fd18f2b9a54c6c8c1ac75cbc3502f230584b155d1c7f1cd45120a653c48d650b431b67c5b2c13f27d7142037c1691027569c503100008000000080000000800001011f00e1f5050000000016001433b982f91b28f160c920b4ab95e58ce50dda3a4a220203309680f33c7de38ea6a47cd4ecd66f1f5a49747c6ffb8808ed09039243e3ad5c47304402202d704ced830c56a909344bd742b6852dccd103e963bae92d38e75254d2bb424502202d86c437195df46c0ceda084f2a291c3da2d64070f76bf9b90b195e7ef28f77201220603309680f33c7de38ea6a47cd4ecd66f1f5a49747c6ffb8808ed09039243e3ad5c1827569c5031000080000000800000008000000000010000000001011f00e1f50500000000160014388fb944307eb77ef45197d0b0b245e079f011de220202c777161f73d0b7c72b9ee7bde650293d13f095bc7656ad1f525da5fd2e10b11047304402204cb1fb5f869c942e0e26100576125439179ae88dca8a9dc3ba08f7953988faa60220521f49ca791c27d70e273c9b14616985909361e25be274ea200d7e08827e514d01220602c777161f73d0b7c72b9ee7bde650293d13f095bc7656ad1f525da5fd2e10b1101827569c5031000080000000800000008000000000000000000000220202d20ca502ee289686d21815bd43a80637b0698e1fbcdbe4caed445f6c1a0a90ef1827569c50310000800000008000000080000000000400000000", +/* + Base64 String: +*/ + "cHNidP8BAJ0BAAAAAnEOp2q0XFy2Q45gflnMA3YmmBgFrp4N/ZCJASq7C+U1AQAAAAD/////GQmU1qizyMgsy8+y+6QQaqBmObhyqNRHRlwNQliNbWcAAAAAAP////8CAOH1BQAAAAAZdqkUtrwsDuVlWoQ9ea/t0MzD991kNAmIrGBa9AUAAAAAFgAUEYjvjkzgRJ6qyPsUHL9aEXbmoIgAAAAATwEEiLIeA55TDKyAAAAAPbyKXJdp8DGxfnf+oVGGAyIaGP0Y8rmlTGyMGsdcvDUC8jBYSxVdHH8c1FEgplPEjWULQxtnxbLBPyfXFCA3wWkQJ1acUDEAAIAAAACAAAAAgAABAR8A4fUFAAAAABYAFDO5gvkbKPFgySC0q5XljOUN2jpKIgIDMJaA8zx9446mpHzU7NZvH1pJdHxv+4gI7QkDkkPjrVxHMEQCIC1wTO2DDFapCTRL10K2hS3M0QPpY7rpLTjnUlTSu0JFAiAthsQ3GV30bAztoITyopHD2i1kBw92v5uQsZXn7yj3cgEiBgMwloDzPH3jjqakfNTs1m8fWkl0fG/7iAjtCQOSQ+OtXBgnVpxQMQAAgAAAAIAAAACAAAAAAAEAAAAAAQEfAOH1BQAAAAAWABQ4j7lEMH63fvRRl9CwskXgefAR3iICAsd3Fh9z0LfHK57nveZQKT0T8JW8dlatH1Jdpf0uELEQRzBEAiBMsftfhpyULg4mEAV2ElQ5F5rojcqKncO6CPeVOYj6pgIgUh9JynkcJ9cOJzybFGFphZCTYeJb4nTqIA1+CIJ+UU0BIgYCx3cWH3PQt8crnue95lApPRPwlbx2Vq0fUl2l/S4QsRAYJ1acUDEAAIAAAACAAAAAgAAAAAAAAAAAAAAiAgLSDKUC7iiWhtIYFb1DqAY3sGmOH7zb5MrtRF9sGgqQ7xgnVpxQMQAAgAAAAIAAAACAAAAAAAQAAAAA"}, +}; diff --git a/src/ctest/test_psbt.c b/src/ctest/test_psbt.c new file mode 100644 index 000000000..1abd75675 --- /dev/null +++ b/src/ctest/test_psbt.c @@ -0,0 +1,96 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "psbts.h" + +static size_t mallocs, frees; + +static void *test_malloc(size_t size) +{ + mallocs++; + return malloc(size); +} + +static void test_free(void *ptr) +{ + if (ptr != NULL) + frees++; + free(ptr); +} + +static const struct wally_operations test_ops = { + test_malloc, test_free, NULL, NULL +}; + +int main(void) +{ + size_t i; + + wally_set_operations(&test_ops); + + for (i = 0; i < sizeof(invalid_psbts) / sizeof(invalid_psbts[0]); i++) { + struct wally_psbt *psbt; + + mallocs = frees = 0; + if (wally_psbt_from_base64(invalid_psbts[i].base64, &psbt) != WALLY_OK) { + if (mallocs != frees) { + errx(1, "Memleak failing parse psbt %s: %zu mallocs, %zu frees", + invalid_psbts[i].base64, mallocs, frees); + } + continue; + } + errx(1, "Should have failed to parse psbt %s", invalid_psbts[i].base64); + } + + for (i = 0; i < sizeof(valid_psbts) / sizeof(valid_psbts[0]); i++) { + struct wally_psbt *psbt; + char *output; + unsigned char *bytes; + size_t len, actual_len; + + mallocs = frees = 0; + if (wally_psbt_from_base64(valid_psbts[i].base64, &psbt) != WALLY_OK) { + errx(1, "Failed to parse psbt %s", valid_psbts[i].base64); + } + if (wally_psbt_to_base64(psbt, &output) != WALLY_OK) { + errx(1, "Failed to base64 psbt %s", valid_psbts[i].base64); + } + if (strcmp(output, valid_psbts[i].base64) != 0) { + errx(1, "psbt %s turned into %s?", valid_psbts[i].base64, output); + } + test_free(output); + if (wally_psbt_get_length(psbt, &len) != WALLY_OK) { + errx(1, "Failed to get pbst %s len", valid_psbts[i].base64); + } + bytes = malloc(len); + if (wally_psbt_to_bytes(psbt, bytes, len, &actual_len) != WALLY_OK) { + errx(1, "psbt %s could not to_bytes?", valid_psbts[i].base64); + } + if (len != actual_len) { + errx(1, "psbt %s to_bytes to %zu not %zu?", valid_psbts[i].base64, + actual_len, len); + } + output = malloc(hex_str_size(len)); + hex_encode(bytes, len, output, hex_str_size(len)); + if (strcmp(output, valid_psbts[i].hex) != 0) { + errx(1, "psbt[%zi] bytes %s not %s", i, output, valid_psbts[i].hex); + } + free(bytes); + free(output); + wally_psbt_free(psbt); + + if (mallocs != frees) { + errx(1, "Memleak parsing psbt %s: %zu mallocs, %zu frees", + valid_psbts[i].base64, mallocs, frees); + } + } + + return 0; +} diff --git a/src/ctest/test_psbt_limits.c b/src/ctest/test_psbt_limits.c new file mode 100644 index 000000000..a1d1f549a --- /dev/null +++ b/src/ctest/test_psbt_limits.c @@ -0,0 +1,144 @@ +/* This is a superset of test_psbt, but requires mmap */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psbts.h" + +static size_t mallocs, frees; + +static void *test_malloc(size_t size) +{ + mallocs++; + return malloc(size); +} + +static void test_free(void *ptr) +{ + if (ptr != NULL) + frees++; + free(ptr); +} + +static const struct wally_operations test_ops = { + test_malloc, test_free, NULL, NULL +}; + +/* Create a cliff: any access past the end will SEGV */ +static unsigned char *cliff(size_t *size) +{ + unsigned char *p; + + /* One page is enough for our tests so far */ + *size = getpagesize(); + + /* MAP_ANON isn't POSIX, but MacOS doesn't let us mmap /dev/zero */ + p = mmap(NULL, *size + getpagesize(), + PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + err(1, "Failed to mmap anon"); + + /* Remove second page. */ + if (munmap(p + *size, getpagesize()) != 0) + err(1, "Failed to munmap /dev/zero"); + return p; +} + +/* Test that we don't read past end of buffer when unmarshalling */ +static void test_psbt_read(const struct psbt_test *test, + unsigned char *p, size_t plen) +{ + size_t i; + + /* It can fit, otherwise adjust cliff() */ + assert(hex_data_size(strlen(test->hex)) <= plen); + + /* Unpack right next to the cliff */ + for (i = 0; i <= hex_data_size(strlen(test->hex)); i++) { + struct wally_psbt *psbt; + size_t bit; + + if (!hex_decode(test->hex, i * 2, p + plen - i, i)) + abort(); + + /* Try it raw: probably will fail. */ + mallocs = frees = 0; + if (wally_psbt_from_bytes(p + plen - i, i, &psbt) == WALLY_OK) + wally_psbt_free(psbt); + if (mallocs != frees) { + errx(1, "psbt %s length %zu: mallocs = %zu, frees = %zu", + test->base64, i, mallocs, frees); + } + + /* Now try flipping each bit in last byte. */ + for (bit = 0; bit < 8; bit++) { + p[plen - 1] ^= (1 << bit); + mallocs = frees = 0; + if (wally_psbt_from_bytes(p + plen - i, i, &psbt) == WALLY_OK) + wally_psbt_free(psbt); + if (mallocs != frees) { + errx(1, "psbt %s length %zu bitfplip %zu: mallocs = %zu, frees = %zu", + test->base64, i, bit, mallocs, frees); + } + p[plen - 1] ^= (1 << bit); + } + } +} + +/* Test that we don't write past end of buffer when marshaling */ +static void test_psbt_write(const struct psbt_test *test, + unsigned char *p, size_t plen) +{ + size_t i, written; + struct wally_psbt *psbt; + + mallocs = frees = 0; + if (wally_psbt_from_base64(test->base64, &psbt) != WALLY_OK) + abort(); + + for (i = 0;; i++) { + if (wally_psbt_to_bytes(psbt, p + plen - i, i, &written) == WALLY_OK) + break; + } + /* Should have fit exactly */ + if (written != i) + errx(1, "wally_psbt_to_bytes %s wrote %zu in %zu bytes?", + test->base64, written, i); + wally_psbt_free(psbt); + if (mallocs != frees) { + errx(1, "psbt write %s: mallocs = %zu, frees = %zu", + test->base64, mallocs, frees); + } +} + +int main(void) +{ + size_t i; + size_t plen; + unsigned char *p = cliff(&plen); + + wally_set_operations(&test_ops); + + for (i = 0; i < sizeof(invalid_psbts) / sizeof(invalid_psbts[0]); i++) { + test_psbt_read(invalid_psbts + i, p, plen); + } + + for (i = 0; i < sizeof(valid_psbts) / sizeof(valid_psbts[0]); i++) { + test_psbt_read(valid_psbts + i, p, plen); + test_psbt_write(valid_psbts + i, p, plen); + } + + return 0; +} diff --git a/src/psbt.c b/src/psbt.c index 34a5e0244..db3bbbe32 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -4,20 +4,27 @@ #include "ccan/ccan/build_assert/build_assert.h" #include +#include #include #include #include #include -#include +#include #include "transaction_shared.h" #include "script_int.h" #include "script.h" +#include "pullpush.h" const uint8_t WALLY_PSBT_MAGIC[5] = {'p', 's', 'b', 't', 0xff}; +#ifdef BUILD_ELEMENTS +const uint8_t WALLY_ELEMENTS_PSBT_MAGIC[5] = {'p', 's', 'e', 't', 0xff}; +const uint8_t WALLY_ELEMENTS_ID[8] = {'e', 'l', 'e', 'm', 'e', 'n', 't', 's'}; +const size_t WALLY_ELEMENTS_ID_LEN = 8; +#endif /* BUILD_ELEMENTS */ -static bool pubkey_is_compressed(unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]) { +static bool pubkey_is_compressed(const unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]) { return pubkey[0] == 0x02 || pubkey[0] == 0x03; } @@ -288,10 +295,10 @@ int wally_unknowns_map_free(struct wally_unknowns_map *unknowns) if (unknowns) { for (i = 0; i < unknowns->num_items; ++i) { if (unknowns->items[i].key) { - wally_clear(unknowns->items[i].key, unknowns->items[i].key_len); + clear_and_free(unknowns->items[i].key, unknowns->items[i].key_len); } if (unknowns->items[i].value) { - wally_clear(unknowns->items[i].value, unknowns->items[i].value_len); + clear_and_free(unknowns->items[i].value, unknowns->items[i].value_len); } } clear_and_free(unknowns->items, unknowns->num_items * sizeof(*unknowns->items)); @@ -441,6 +448,94 @@ int wally_psbt_input_init_alloc( return ret; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_input_init_alloc( + struct wally_tx *non_witness_utxo, + struct wally_tx_output *witness_utxo, + unsigned char *redeem_script, + size_t redeem_script_len, + unsigned char *witness_script, + size_t witness_script_len, + unsigned char *final_script_sig, + size_t final_script_sig_len, + struct wally_tx_witness_stack *final_witness, + struct wally_keypath_map *keypaths, + struct wally_partial_sigs_map *partial_sigs, + struct wally_unknowns_map *unknowns, + uint32_t sighash_type, + uint64_t value, + bool has_value, + unsigned char *value_blinder, + size_t value_blinder_len, + unsigned char *asset, + size_t asset_len, + unsigned char *asset_blinder, + size_t asset_blinder_len, + struct wally_tx *peg_in_tx, + unsigned char *txout_proof, + size_t txout_proof_len, + unsigned char *genesis_hash, + size_t genesis_hash_len, + unsigned char *claim_script, + size_t claim_script_len, + struct wally_psbt_input **output) +{ + struct wally_psbt_input *result; + int ret = wally_psbt_input_init_alloc(non_witness_utxo, + witness_utxo, + redeem_script, + redeem_script_len, + witness_script, + witness_script_len, + final_script_sig, + final_script_sig_len, + final_witness, + keypaths, + partial_sigs, + unknowns, + sighash_type, + output); + if (ret != WALLY_OK) { + return ret; + } + + /* wally_psbt_input_init_alloc will allocate and initialize the input */ + result = *output; + + if (has_value && (ret = wally_psbt_elements_input_set_value(result, value)) != WALLY_OK) { + goto fail; + } + if (value_blinder && (ret = wally_psbt_elements_input_set_value_blinder(result, value_blinder, value_blinder_len)) != WALLY_OK) { + goto fail; + } + if (asset && (ret = wally_psbt_elements_input_set_asset(result, asset, asset_len)) != WALLY_OK) { + goto fail; + } + if (asset_blinder && (ret = wally_psbt_elements_input_set_asset_blinder(result, asset_blinder, asset_blinder_len)) != WALLY_OK) { + goto fail; + } + if (peg_in_tx && (ret = wally_psbt_elements_input_set_peg_in_tx(result, peg_in_tx)) != WALLY_OK) { + goto fail; + } + if (txout_proof && (ret = wally_psbt_elements_input_set_txout_proof(result, txout_proof, txout_proof_len)) != WALLY_OK) { + goto fail; + } + if (genesis_hash && (ret = wally_psbt_elements_input_set_genesis_hash(result, genesis_hash, genesis_hash_len)) != WALLY_OK) { + goto fail; + } + if (claim_script && (ret = wally_psbt_elements_input_set_claim_script(result, claim_script, claim_script_len)) != WALLY_OK) { + goto fail; + } + + return ret; + +fail: + wally_psbt_input_free(result); + *output = NULL; + return ret; +} +#endif /* BUILD_ELEMENTS */ + int wally_psbt_input_set_non_witness_utxo( struct wally_psbt_input *input, struct wally_tx *non_witness_utxo) @@ -460,15 +555,21 @@ int wally_psbt_input_set_witness_utxo( struct wally_psbt_input *input, struct wally_tx_output *witness_utxo) { - int ret = WALLY_OK; - struct wally_tx_output *result_witness_utxo; + int res = WALLY_OK; + struct wally_tx_output *output; - if ((ret = wally_tx_output_init_alloc(witness_utxo->satoshi, witness_utxo->script, witness_utxo->script_len, &result_witness_utxo)) != WALLY_OK) { - return ret; + output = wally_malloc(sizeof(struct wally_tx_output)); + if (!output) return WALLY_ENOMEM; + wally_clear((void *)output, sizeof(struct wally_tx_output)); + + /* Since Elements has a different setup, + * we just clone the passed in output */ + if ((res = wally_clone_output_to(output, witness_utxo)) != WALLY_OK) { + return res; } wally_tx_output_free(input->witness_utxo); - input->witness_utxo = result_witness_utxo; - return ret; + input->witness_utxo = output; + return res; } int wally_psbt_input_set_redeem_script( @@ -583,6 +684,129 @@ int wally_psbt_input_set_sighash_type( return WALLY_OK; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_input_set_value( + struct wally_psbt_input *input, + uint64_t value) +{ + input->value = value; + input->has_value = true; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_value_blinder( + struct wally_psbt_input *input, + unsigned char *value_blinder, + size_t value_blinder_len) +{ + unsigned char *result_value_blinder; + + if (!clone_bytes(&result_value_blinder, value_blinder, value_blinder_len)) { + return WALLY_ENOMEM; + } + wally_free(input->value_blinder); + input->value_blinder = result_value_blinder; + input->value_blinder_len = value_blinder_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_asset( + struct wally_psbt_input *input, + unsigned char *asset, + size_t asset_len) +{ + unsigned char *result_asset; + + if (!clone_bytes(&result_asset, asset, asset_len)) { + return WALLY_ENOMEM; + } + wally_free(input->asset); + input->asset = result_asset; + input->asset_len = asset_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_asset_blinder( + struct wally_psbt_input *input, + unsigned char *asset_blinder, + size_t asset_blinder_len) +{ + unsigned char *result_asset_blinder; + + if (!clone_bytes(&result_asset_blinder, asset_blinder, asset_blinder_len)) { + return WALLY_ENOMEM; + } + wally_free(input->asset_blinder); + input->asset_blinder = result_asset_blinder; + input->asset_blinder_len = asset_blinder_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_peg_in_tx( + struct wally_psbt_input *input, + struct wally_tx *peg_in_tx) +{ + int ret = WALLY_OK; + struct wally_tx *result_peg_in_tx; + + if ((ret = clone_tx(peg_in_tx, &result_peg_in_tx)) != WALLY_OK) { + return ret; + } + wally_tx_free(input->peg_in_tx); + input->peg_in_tx = result_peg_in_tx; + return ret; +} + + +int wally_psbt_elements_input_set_txout_proof( + struct wally_psbt_input *input, + unsigned char *txout_proof, + size_t txout_proof_len) +{ + unsigned char *result_txout_proof; + + if (!clone_bytes(&result_txout_proof, txout_proof, txout_proof_len)) { + return WALLY_ENOMEM; + } + wally_free(input->txout_proof); + input->txout_proof = result_txout_proof; + input->txout_proof_len = txout_proof_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_genesis_hash( + struct wally_psbt_input *input, + unsigned char *genesis_hash, + size_t genesis_hash_len) +{ + unsigned char *result_genesis_hash; + + if (!clone_bytes(&result_genesis_hash, genesis_hash, genesis_hash_len)) { + return WALLY_ENOMEM; + } + wally_free(input->genesis_hash); + input->genesis_hash = result_genesis_hash; + input->genesis_hash_len = genesis_hash_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_claim_script( + struct wally_psbt_input *input, + unsigned char *claim_script, + size_t claim_script_len) +{ + unsigned char *result_claim_script; + + if (!clone_bytes(&result_claim_script, claim_script, claim_script_len)) { + return WALLY_ENOMEM; + } + wally_free(input->claim_script); + input->claim_script = result_claim_script; + input->claim_script_len = claim_script_len; + return WALLY_OK; +} +#endif /* BUILD_ELEMENTS */ + int wally_psbt_input_free(struct wally_psbt_input *input) { if (input) { @@ -595,6 +819,15 @@ int wally_psbt_input_free(struct wally_psbt_input *input) wally_keypath_map_free(input->keypaths); wally_partial_sigs_map_free(input->partial_sigs); wally_unknowns_map_free(input->unknowns); +#ifdef BUILD_ELEMENTS + clear_and_free(input->value_blinder, input->value_blinder_len); + clear_and_free(input->asset, input->asset_len); + clear_and_free(input->asset_blinder, input->asset_blinder_len); + wally_tx_free(input->peg_in_tx); + clear_and_free(input->txout_proof, input->txout_proof_len); + clear_and_free(input->genesis_hash, input->genesis_hash_len); + clear_and_free(input->claim_script, input->claim_script_len); +#endif /* BUILD_ELEMENTS */ } return WALLY_OK; } @@ -635,6 +868,81 @@ int wally_psbt_output_init_alloc( return ret; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_output_init_alloc( + unsigned char *redeem_script, + size_t redeem_script_len, + unsigned char *witness_script, + size_t witness_script_len, + struct wally_keypath_map *keypaths, + struct wally_unknowns_map *unknowns, + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN], + bool has_blinding_pubkey, + unsigned char *value_commitment, + size_t value_commitment_len, + unsigned char *value_blinder, + size_t value_blinder_len, + unsigned char *asset_commitment, + size_t asset_commitment_len, + unsigned char *asset_blinder, + size_t asset_blinder_len, + unsigned char *nonce_commitment, + size_t nonce_commitment_len, + unsigned char *range_proof, + size_t range_proof_len, + unsigned char *surjection_proof, + size_t surjection_proof_len, + struct wally_psbt_output **output) +{ + struct wally_psbt_output *result; + int ret = wally_psbt_output_init_alloc(redeem_script, + redeem_script_len, + witness_script, + witness_script_len, + keypaths, + unknowns, + output); + if (ret != WALLY_OK) { + return ret; + } + + /* wally_psbt_output_init_alloc allocates for us */ + result = *output; + + if (has_blinding_pubkey && (ret = wally_psbt_elements_output_set_blinding_pubkey(result, blinding_pubkey)) != WALLY_OK) { + goto fail; + } + if (value_commitment && (ret = wally_psbt_elements_output_set_value_commitment(result, value_commitment, value_commitment_len)) != WALLY_OK) { + goto fail; + } + if (value_blinder && (ret = wally_psbt_elements_output_set_value_blinder(result, value_blinder, value_blinder_len)) != WALLY_OK) { + goto fail; + } + if (asset_commitment && (ret = wally_psbt_elements_output_set_asset_commitment(result, asset_commitment, asset_commitment_len)) != WALLY_OK) { + goto fail; + } + if (asset_blinder && (ret = wally_psbt_elements_output_set_asset_blinder(result, asset_blinder, asset_blinder_len)) != WALLY_OK) { + goto fail; + } + if (nonce_commitment && (ret = wally_psbt_elements_output_set_nonce_commitment(result, nonce_commitment, nonce_commitment_len)) != WALLY_OK) { + goto fail; + } + if (range_proof && (ret = wally_psbt_elements_output_set_range_proof(result, range_proof, range_proof_len)) != WALLY_OK) { + goto fail; + } + if (surjection_proof && (ret = wally_psbt_elements_output_set_surjection_proof(result, surjection_proof, surjection_proof_len)) != WALLY_OK) { + goto fail; + } + + return ret; + +fail: + wally_psbt_output_free(result); + *output = NULL; + return ret; +} +#endif /* BUILD_ELEMENTS */ + int wally_psbt_output_set_redeem_script( struct wally_psbt_output *output, unsigned char *redeem_script, @@ -695,6 +1003,129 @@ int wally_psbt_output_set_unknowns( return WALLY_OK; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_output_set_blinding_pubkey( + struct wally_psbt_output *output, + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]) +{ + memcpy(output->blinding_pubkey, blinding_pubkey, EC_PUBLIC_KEY_UNCOMPRESSED_LEN); + output->has_blinding_pubkey = true; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_value_commitment( + struct wally_psbt_output *output, + unsigned char *value_commitment, + size_t value_commitment_len) +{ + unsigned char *result_value_commitment; + + if (!clone_bytes(&result_value_commitment, value_commitment, value_commitment_len)) { + return WALLY_ENOMEM; + } + wally_free(output->value_commitment); + output->value_commitment = value_commitment; + output->value_commitment_len = value_commitment_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_value_blinder( + struct wally_psbt_output *output, + unsigned char *value_blinder, + size_t value_blinder_len) +{ + unsigned char *result_value_blinder; + + if (!clone_bytes(&result_value_blinder, value_blinder, value_blinder_len)) { + return WALLY_ENOMEM; + } + wally_free(output->value_blinder); + output->value_blinder = value_blinder; + output->value_blinder_len = value_blinder_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_asset_commitment( + struct wally_psbt_output *output, + unsigned char *asset_commitment, + size_t asset_commitment_len) +{ + unsigned char *result_asset_commitment; + + if (!clone_bytes(&result_asset_commitment, asset_commitment, asset_commitment_len)) { + return WALLY_ENOMEM; + } + wally_free(output->asset_commitment); + output->asset_commitment = asset_commitment; + output->asset_commitment_len = asset_commitment_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_asset_blinder( + struct wally_psbt_output *output, + unsigned char *asset_blinder, + size_t asset_blinder_len) +{ + unsigned char *result_asset_blinder; + + if (!clone_bytes(&result_asset_blinder, asset_blinder, asset_blinder_len)) { + return WALLY_ENOMEM; + } + wally_free(output->asset_blinder); + output->asset_blinder = asset_blinder; + output->asset_blinder_len = asset_blinder_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_nonce_commitment( + struct wally_psbt_output *output, + unsigned char *nonce_commitment, + size_t nonce_commitment_len) +{ + unsigned char *result_nonce_commitment; + + if (!clone_bytes(&result_nonce_commitment, nonce_commitment, nonce_commitment_len)) { + return WALLY_ENOMEM; + } + wally_free(output->nonce_commitment); + output->nonce_commitment = nonce_commitment; + output->nonce_commitment_len = nonce_commitment_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_range_proof( + struct wally_psbt_output *output, + unsigned char *range_proof, + size_t range_proof_len) +{ + unsigned char *result_range_proof; + + if (!clone_bytes(&result_range_proof, range_proof, range_proof_len)) { + return WALLY_ENOMEM; + } + wally_free(output->range_proof); + output->range_proof = range_proof; + output->range_proof_len = range_proof_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_surjection_proof( + struct wally_psbt_output *output, + unsigned char *surjection_proof, + size_t surjection_proof_len) +{ + unsigned char *result_surjection_proof; + + if (!clone_bytes(&result_surjection_proof, surjection_proof, surjection_proof_len)) { + return WALLY_ENOMEM; + } + wally_free(output->surjection_proof); + output->surjection_proof = surjection_proof; + output->surjection_proof_len = surjection_proof_len; + return WALLY_OK; +} +#endif/* BUILD_ELEMENTS */ + int wally_psbt_output_free(struct wally_psbt_output *output) { if (output) { @@ -702,6 +1133,15 @@ int wally_psbt_output_free(struct wally_psbt_output *output) clear_and_free(output->witness_script, output->witness_script_len); wally_keypath_map_free(output->keypaths); wally_unknowns_map_free(output->unknowns); +#ifdef BUILD_ELEMENTS + clear_and_free(output->value_commitment, output->value_commitment_len); + clear_and_free(output->value_blinder, output->value_blinder_len); + clear_and_free(output->asset_commitment, output->asset_commitment_len); + clear_and_free(output->asset_blinder, output->asset_blinder_len); + clear_and_free(output->nonce_commitment, output->nonce_commitment_len); + clear_and_free(output->range_proof, output->range_proof_len); + clear_and_free(output->surjection_proof, output->surjection_proof_len); +#endif /* BUILD_ELEMENTS */ } return WALLY_OK; } @@ -739,6 +1179,11 @@ int wally_psbt_init_alloc( return WALLY_ENOMEM; } + /* Version is always 0 */ + result->version = 0; + + memcpy(result->magic, WALLY_PSBT_MAGIC, 5); + result->inputs = new_inputs; result->num_inputs = 0; result->inputs_allocation_len = inputs_allocation_len; @@ -749,6 +1194,22 @@ int wally_psbt_init_alloc( return WALLY_OK; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_init_alloc( + size_t inputs_allocation_len, + size_t outputs_allocation_len, + size_t global_unknowns_allocation_len, + struct wally_psbt **output) +{ + int ret; + + ret = wally_psbt_init_alloc(inputs_allocation_len, outputs_allocation_len, global_unknowns_allocation_len, output); + memcpy((*output)->magic, WALLY_ELEMENTS_PSBT_MAGIC, 5); + + return ret; +} +#endif /* BUILD_ELEMENTS */ + int wally_psbt_free(struct wally_psbt *psbt) { size_t i; @@ -831,6 +1292,225 @@ int wally_psbt_set_global_tx( return ret; } +/* Returns false if it hits a zero length "key" (i.e. separator) or EOF. + * + * Otherwise, starts the subfield (extra, extra_len), so caller should + * call pull_subfield_end(cursor, max, extra, extra_len) if this + * returns true. + */ +static bool pull_psbt_key_start( + const unsigned char **cursor, size_t *max, + uint64_t *type, + const unsigned char **extra, size_t *extra_len) +{ + size_t key_len; + + key_len = pull_varlength(cursor, max); + /* This incidentally covers the case where *cursor is NULL */ + if (key_len == 0) { + return false; + } + pull_subfield_start(cursor, max, key_len, extra, extra_len); + *type = pull_varint(cursor, max); + return true; +} + +/* clones varlength field entirely. */ +static bool clone_varlength(unsigned char **dst, + size_t *len, + const unsigned char **cursor, size_t *max) +{ + *len = pull_varlength(cursor, max); + return clone_bytes(dst, pull_skip(cursor, max, *len), *len); +} + +/* Stricter version of pull_subfield_end which insists there's nothing left. */ +static void subfield_nomore_end(const unsigned char **cursor, size_t *max, + const unsigned char *subcursor, + const size_t submax) +{ + if (submax) { + pull_failed(cursor, max); + } else { + pull_subfield_end(cursor, max, subcursor, submax); + } +} + +/* The remainder of the key is a public key, the value is a keypath */ +static int pull_keypath(const unsigned char **cursor, size_t *max, + const unsigned char *key, size_t key_len, + struct wally_keypath_map *keypaths) +{ + const unsigned char *val; + size_t i, val_max; + struct wally_keypath_item *kpitem; + + if (key_len != EC_PUBLIC_KEY_UNCOMPRESSED_LEN + && key_len != EC_PUBLIC_KEY_LEN) { + return WALLY_EINVAL; /* Size of key is unexpected */ + } + + /* Check for duplicates */ + for (i = 0; i < keypaths->num_items; ++i) { + if (memcmp(keypaths->items[i].pubkey, key, key_len) == 0) { + return WALLY_EINVAL; /* Duplicate key */ + } + } + + assert(keypaths->num_items < keypaths->items_allocation_len); + kpitem = &keypaths->items[keypaths->num_items++]; + + memcpy(kpitem->pubkey, key, key_len); + pull_subfield_end(cursor, max, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, pull_varint(cursor, max), &val, &val_max); + + /* Read the fingerprint */ + pull_bytes(kpitem->origin.fingerprint, sizeof(kpitem->origin.fingerprint), + &val, &val_max); + + /* Remainder is the path */ + kpitem->origin.path_len = val_max / sizeof(uint32_t); + kpitem->origin.path = wally_malloc(val_max); + if (kpitem->origin.path == NULL) { + return WALLY_ENOMEM; + } + for (i = 0; val_max >= sizeof(uint32_t); ++i) { + kpitem->origin.path[i] = pull_le32(&val, &val_max); + } + subfield_nomore_end(cursor, max, val, val_max); + return WALLY_OK; +} + +/* Rewind cursor to prekey, and append unknown key/value to unknowns */ +static int pull_unknown_key_value(const unsigned char **cursor, + size_t *max, + const unsigned char *pre_key, + struct wally_unknowns_map *unknowns) +{ + struct wally_unknowns_item *item; + + /* If we've already failed, it's invalid */ + if (!*cursor) { + return WALLY_EINVAL; + } + + /* We have to unwind a bit, to get entire key again. */ + *max += (*cursor - pre_key); + *cursor = pre_key; + + assert(unknowns->num_items < unknowns->items_allocation_len); + item = &unknowns->items[unknowns->num_items++]; + + if (!clone_varlength(&item->key, &item->key_len, cursor, max)) { + return WALLY_ENOMEM; + } + if (!clone_varlength(&item->value, &item->value_len, cursor, max)) { + return WALLY_ENOMEM; + } + return WALLY_OK; +} + +#ifdef BUILD_ELEMENTS +static size_t push_elements_bytes_size(const struct wally_tx_output *out) +{ + size_t size = 0; + size += out->asset_len == 0 ? 1 : out->asset_len; + size += out->value_len == 0 ? 1 : out->value_len; + size += out->nonce_len == 0 ? 1 : out->nonce_len; + size += out->script_len == 0 ? 1 : out->script_len + 1; + return size; +} +static void push_elements_bytes(unsigned char **cursor, + size_t *max, + unsigned char *value, + size_t val_len) +{ + if (value == NULL) { + unsigned char empty[1] = { 0x00 }; + push_bytes(cursor, max, empty, sizeof(empty)); + } else { + push_bytes(cursor, max, + value, val_len); + } +} + +static int pull_elements_confidential(const unsigned char **cursor, + size_t *max, + unsigned char **value, + size_t *val_len, + size_t prefixA, size_t prefixB, + size_t prefixedSize, size_t explicitSize) +{ + uint8_t type = peek_u8(cursor, max); + /*The first byte is always the 'version' + * which tells you what the value is */ + switch (type) { + /* Empty */ + case 0: + /* Pop off the type */ + pull_u8(cursor, max); + *value = NULL; + *val_len = 0; + return WALLY_OK; + /* Explicit size */ + case 1: + *value = wally_malloc(explicitSize); + pull_bytes(*value, explicitSize, cursor, max); + if (!*cursor) + return WALLY_EINVAL; + *val_len = explicitSize; + return WALLY_OK; + } + if (type == prefixA || type == prefixB) { + *value = wally_malloc(prefixedSize); + pull_bytes(*value, prefixedSize, cursor, max); + if (!*cursor) + return WALLY_EINVAL; + *val_len = prefixedSize; + return WALLY_OK; + } + return WALLY_EINVAL; +} + +/* Either returns a 33-byte commitment to a confidential value, or + * a 64-bit explicit value. */ +static int pull_confidential_value(const unsigned char **cursor, + size_t *max, + unsigned char **value, + size_t *val_len) + +{ + return pull_elements_confidential(cursor, max, value, val_len, + WALLY_TX_ASSET_CT_VALUE_PREFIX_A, WALLY_TX_ASSET_CT_VALUE_PREFIX_B, + WALLY_TX_ASSET_CT_VALUE_LEN, WALLY_TX_ASSET_CT_VALUE_UNBLIND_LEN); +} + +static int pull_confidential_asset(const unsigned char **cursor, + size_t *max, + unsigned char **asset, + size_t *asset_len) + +{ + return pull_elements_confidential(cursor, max, asset, asset_len, + WALLY_TX_ASSET_CT_ASSET_PREFIX_A, WALLY_TX_ASSET_CT_ASSET_PREFIX_B, + WALLY_TX_ASSET_CT_ASSET_LEN, WALLY_TX_ASSET_CT_ASSET_LEN); +} + +static int pull_nonce(const unsigned char **cursor, + size_t *max, + unsigned char **nonce, + size_t *nonce_len) + +{ + return pull_elements_confidential(cursor, max, nonce, nonce_len, + WALLY_TX_ASSET_CT_NONCE_PREFIX_A, WALLY_TX_ASSET_CT_NONCE_PREFIX_B, + WALLY_TX_ASSET_CT_NONCE_LEN, WALLY_TX_ASSET_CT_NONCE_LEN); +} + +#endif /* BUILD_ELEMENTS */ + struct psbt_input_counts { size_t num_unknowns; size_t num_keypaths; @@ -859,24 +1539,15 @@ static void free_psbt_count(struct psbt_counts *counts) } } -/* Check that the bytes already read + bytes to be read < total len */ -#define CHECK_BUF_BOUNDS(p, i, begin, tl) if ((p - begin) + i > tl) { \ - ret = WALLY_EINVAL; \ - goto fail; \ -} - static int count_psbt_parts( const unsigned char *bytes, size_t bytes_len, + bool is_elements, struct psbt_counts **output) { + int ret; + size_t i, key_len; struct psbt_counts *result; - size_t i, vl; - const unsigned char *key; - uint64_t key_len, value_len; - uint8_t type; - int ret = WALLY_OK; - const unsigned char *p = bytes, *end = bytes + bytes_len; TX_CHECK_OUTPUT; TX_OUTPUT_ALLOC(struct psbt_counts); @@ -885,39 +1556,36 @@ static int count_psbt_parts( result->num_inputs = 0; result->num_outputs = 0; - /* Skip the magic */ - CHECK_BUF_BOUNDS(p, (size_t)5, bytes, bytes_len) - p += 5; - /* Go through globals and count */ - while (p < end) { - /* Read the key length */ - vl = varint_from_bytes(p, &key_len); - CHECK_BUF_BOUNDS(p, key_len + vl, bytes, bytes_len) - p += vl; - - if (key_len == 0) { - break; - } + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { + const unsigned char *key; - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; - - /* Read value length */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) - p += vl; + /* Start parsing key */ + pull_subfield_start(&bytes, &bytes_len, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_GLOBAL_UNSIGNED_TX: { bool expect_wit; - if (analyze_tx(p, value_len, 0, &result->num_inputs, &result->num_outputs, &expect_wit) != WALLY_OK) { + const unsigned char *val; + size_t val_max; + int flags = 0; + subfield_nomore_end(&bytes, &bytes_len, key, key_len); + + /* Value should be a tx */ + val_max = pull_varint(&bytes, &bytes_len); + val = pull_skip(&bytes, &bytes_len, val_max); + if (!val) { ret = WALLY_EINVAL; goto fail; } + if (is_elements) { + flags |= WALLY_TX_FLAG_USE_ELEMENTS; + } + ret = analyze_tx(val, val_max, flags, &result->num_inputs, &result->num_outputs, &expect_wit); + if (ret != WALLY_OK) { + goto fail; + } if ((result->input_counts = wally_malloc(result->num_inputs * sizeof(struct psbt_input_counts))) == NULL || (result->output_counts = wally_malloc(result->num_outputs * sizeof(struct psbt_output_counts))) == NULL) { ret = WALLY_ENOMEM; @@ -925,43 +1593,35 @@ static int count_psbt_parts( } break; } + case WALLY_PSBT_GLOBAL_VERSION: + pull_subfield_end(&bytes, &bytes_len, key, key_len); + /* Skip over value */ + pull_skip(&bytes, &bytes_len, pull_varint(&bytes, &bytes_len)); + break; /* Unknowns */ default: result->num_global_unknowns++; + pull_subfield_end(&bytes, &bytes_len, key, key_len); + /* Skip over value */ + pull_skip(&bytes, &bytes_len, pull_varint(&bytes, &bytes_len)); } - - /* Increment past value length */ - p += value_len; } /* Go through each input */ - for (i = 0; i < result->num_inputs && p < end; ++i) { + for (i = 0; i < result->num_inputs; ++i) { struct psbt_input_counts *input = &result->input_counts[i]; input->num_keypaths = 0; input->num_partial_sigs = 0; input->num_unknowns = 0; - while (p < end) { - /* Read the key length */ - vl = varint_from_bytes(p, &key_len); - CHECK_BUF_BOUNDS(p, key_len + vl, bytes, bytes_len) - p += vl; - - if (key_len == 0) { - break; - } - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { + const unsigned char *key; - /* Read value length */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) - p += vl; + /* Start parsing key */ + pull_subfield_start(&bytes, &bytes_len, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_IN_NON_WITNESS_UTXO: case WALLY_PSBT_IN_WITNESS_UTXO: case WALLY_PSBT_IN_SIGHASH_TYPE: @@ -976,60 +1636,111 @@ static int count_psbt_parts( case WALLY_PSBT_IN_BIP32_DERIVATION: input->num_keypaths++; break; + case WALLY_PSBT_PROPRIETARY_TYPE: { +#ifdef BUILD_ELEMENTS + uint64_t id_len; + bool valid_type = false; + + id_len = pull_varlength(&key, &key_len); + if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { + pull_skip(&key, &key_len, id_len); + + switch (pull_varint(&key, &key_len)) { + case WALLY_PSBT_IN_ELEMENTS_VALUE: + case WALLY_PSBT_IN_ELEMENTS_VALUE_BLINDER: + case WALLY_PSBT_IN_ELEMENTS_ASSET: + case WALLY_PSBT_IN_ELEMENTS_ASSET_BLINDER: + case WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX: + case WALLY_PSBT_IN_ELEMENTS_TXOUT_PROOF: + case WALLY_PSBT_IN_ELEMENTS_GENESIS_HASH: + case WALLY_PSBT_IN_ELEMENTS_CLAIM_SCRIPT: + valid_type = true; + break; + default: + valid_type = false; + } + } + if (valid_type) { + break; + } +#endif /* BUILD_ELEMENTS */ + } + /* For unknown case without elements or for unknown proprietary types */ + /* fall through */ /* Unknowns */ default: input->num_unknowns++; } - - /* Increment past value length */ - p += value_len; + pull_subfield_end(&bytes, &bytes_len, key, key_len); + /* Skip over value */ + pull_skip(&bytes, &bytes_len, pull_varint(&bytes, &bytes_len)); } } /* Go through each output */ - for (i = 0; i < result->num_outputs && p < end; ++i) { + for (i = 0; i < result->num_outputs; ++i) { struct psbt_output_counts *psbt_output = &result->output_counts[i]; psbt_output->num_keypaths = 0; psbt_output->num_unknowns = 0; - while (p < end) { - /* Read the key length */ - vl = varint_from_bytes(p, &key_len); - CHECK_BUF_BOUNDS(p, key_len + vl, bytes, bytes_len) - p += vl; - - if (key_len == 0) { - break; - } - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { + const unsigned char *key; - /* Read value length */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) - p += vl; + /* Start parsing key */ + pull_subfield_start(&bytes, &bytes_len, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_OUT_REDEEM_SCRIPT: case WALLY_PSBT_OUT_WITNESS_SCRIPT: break; case WALLY_PSBT_OUT_BIP32_DERIVATION: psbt_output->num_keypaths++; break; + case WALLY_PSBT_PROPRIETARY_TYPE: { +#ifdef BUILD_ELEMENTS + uint64_t id_len; + bool valid_type = false; + + id_len = pull_varlength(&key, &key_len); + if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { + pull_skip(&key, &key_len, id_len); + + switch (pull_varint(&key, &key_len)) { + case WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT: + case WALLY_PSBT_OUT_ELEMENTS_VALUE_BLINDER: + case WALLY_PSBT_OUT_ELEMENTS_ASSET_COMMITMENT: + case WALLY_PSBT_OUT_ELEMENTS_ASSET_BLINDER: + case WALLY_PSBT_OUT_ELEMENTS_RANGE_PROOF: + case WALLY_PSBT_OUT_ELEMENTS_SURJECTION_PROOF: + case WALLY_PSBT_OUT_ELEMENTS_BLINDING_PUBKEY: + case WALLY_PSBT_OUT_ELEMENTS_NONCE_COMMITMENT: + valid_type = true; + break; + default: + valid_type = false; + } + } + if (valid_type) { + break; + } +#endif /* BUILD_ELEMENTS */ + } + /* For unknown case without elements or for unknown proprietary types */ + /* fall through */ /* Unknowns */ default: psbt_output->num_unknowns++; } - /* Increment past value length */ - p += value_len; + pull_subfield_end(&bytes, &bytes_len, key, key_len); + /* Skip over value */ + pull_skip(&bytes, &bytes_len, pull_varint(&bytes, &bytes_len)); } } - if (p != end) { + /* Either we ran short, or had too much? */ + if (bytes == NULL || bytes_len != 0) { ret = WALLY_EINVAL; goto fail; } @@ -1042,19 +1753,16 @@ static int count_psbt_parts( return ret; } -static int psbt_input_from_bytes( - const unsigned char *bytes, - size_t bytes_len, +static int pull_psbt_input( + const unsigned char **cursor, + size_t *max, struct psbt_input_counts counts, - size_t *bytes_read, + bool is_elements, struct wally_psbt_input *result) { - const unsigned char *p = bytes, *end = bytes + bytes_len, *key, *value; - uint64_t key_len, value_len; - uint8_t type; - int ret = WALLY_OK; - size_t i, vl; - bool found_sep = false; + int ret; + size_t key_len; + const unsigned char *pre_key; /* Init and alloc the maps */ if (counts.num_keypaths > 0) { @@ -1074,235 +1782,405 @@ static int psbt_input_from_bytes( } /* Read key value pairs */ - while (p < end) { - /* Read the key length */ - vl = varint_from_bytes(p, &key_len); - CHECK_BUF_BOUNDS(p, key_len + vl, bytes, bytes_len) - p += vl; - - if (key_len == 0) { - found_sep = true; - break; - } + pre_key = *cursor; + while ((key_len = pull_varlength(cursor, max)) != 0) { + const unsigned char *key, *val; + size_t val_max; - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; - - /* Pre-read the value length but don't increment for a sanity check */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) + /* Start parsing key */ + pull_subfield_start(cursor, max, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_IN_NON_WITNESS_UTXO: { + int flags = 0; if (result->non_witness_utxo) { return WALLY_EINVAL; /* We already have a non witness utxo */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Global tx key is one byte type */ } - p += varint_from_bytes(p, &value_len); - value = p; - wally_tx_from_bytes(value, value_len, 0, &result->non_witness_utxo); - p += value_len; + subfield_nomore_end(cursor, max, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); + if (is_elements) { + flags |= WALLY_TX_FLAG_USE_ELEMENTS; + } + ret = wally_tx_from_bytes(val, val_max, flags, + &result->non_witness_utxo); + if (ret != WALLY_OK) { + return ret; + } + pull_subfield_end(cursor, max, val, val_max); break; } case WALLY_PSBT_IN_WITNESS_UTXO: { - uint64_t amount = -1, script_len; - size_t script_len_len; + uint64_t amount, script_len; + const unsigned char *script; if (result->witness_utxo) { return WALLY_EINVAL; /* We already have a witness utxo */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Global tx key is one byte type */ - } - p += varint_from_bytes(p, &value_len); - p += uint64_from_le_bytes(p, &amount); - script_len_len = varint_from_bytes(p, &script_len); - p += script_len_len; - ret = wally_tx_output_init_alloc(amount, p, script_len, &result->witness_utxo); - if (ret != WALLY_OK) { - return ret; } - p += script_len; - /* amount length (8 bytes) + script CSUint + script length = value length */ - if (8 + script_len_len + script_len != value_len) { + subfield_nomore_end(cursor, max, key, key_len); + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); +#ifdef BUILD_ELEMENTS + if (is_elements) { + unsigned char *asset, *value, *nonce; + size_t asset_len, value_len, nonce_len; + if ((ret = pull_confidential_asset(&val, &val_max, &asset, &asset_len)) != WALLY_OK) { + return ret; + } + if ((ret = pull_confidential_value(&val, &val_max, &value, &value_len)) != WALLY_OK) { + return ret; + } + if ((ret = pull_nonce(&val, &val_max, &nonce, &nonce_len)) != WALLY_OK) { + return ret; + } + script_len = pull_varint(&val, &val_max); + script = pull_skip(&val, &val_max, script_len); + if (!script) { + return WALLY_EINVAL; + } + ret = wally_tx_elements_output_init_alloc(script, script_len, + asset, asset_len, + value, value_len, + nonce, nonce_len, + NULL, 0, NULL, 0, + &result->witness_utxo); + if (ret != WALLY_OK) { + return ret; + } + subfield_nomore_end(cursor, max, val, val_max); + break; + } +#endif /* BUILD_ELEMENTS */ + + amount = pull_le64(&val, &val_max); + script_len = pull_varint(&val, &val_max); + script = pull_skip(&val, &val_max, script_len); + if (!script) { return WALLY_EINVAL; } + ret = wally_tx_output_init_alloc(amount, script, script_len, + &result->witness_utxo); + if (ret != WALLY_OK) { + return ret; + } + subfield_nomore_end(cursor, max, val, val_max); break; } case WALLY_PSBT_IN_PARTIAL_SIG: { + size_t i; + struct wally_partial_sigs_item *sigitem; struct wally_partial_sigs_map *partial_sigs = result->partial_sigs; - if (key_len != 66 && key_len != 34) { + if (key_len != EC_PUBLIC_KEY_UNCOMPRESSED_LEN + && key_len != EC_PUBLIC_KEY_LEN) { return WALLY_EINVAL; /* Size of key is unexpected */ } /* Check for duplicates */ for (i = 0; i < partial_sigs->num_items; ++i) { - if (memcmp(partial_sigs->items[i].pubkey, &key[1], key_len - 1) == 0) { + if (memcmp(partial_sigs->items[i].pubkey, key, key_len) == 0) { return WALLY_EINVAL; /* Duplicate key */ } } - memcpy(partial_sigs->items[partial_sigs->num_items].pubkey, &key[1], key_len - 1); - - /* Read the signature */ - p += varint_from_bytes(p, &value_len); - clone_bytes(&partial_sigs->items[partial_sigs->num_items].sig, p, value_len); - partial_sigs->items[partial_sigs->num_items].sig_len = value_len; + sigitem = &partial_sigs->items[partial_sigs->num_items]; + memcpy(sigitem->pubkey, key, key_len); + pull_subfield_end(cursor, max, key, key_len); + if (!clone_varlength(&sigitem->sig, &sigitem->sig_len, + cursor, max)) { + return WALLY_ENOMEM; + } partial_sigs->num_items++; - p += value_len; break; } case WALLY_PSBT_IN_SIGHASH_TYPE: { if (result->sighash_type > 0) { return WALLY_EINVAL; /* Sighash already provided */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Type is more than one byte */ } - p += varint_from_bytes(p, &value_len); - p += uint32_from_le_bytes(p, &result->sighash_type); + subfield_nomore_end(cursor, max, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); + result->sighash_type = pull_le32(&val, &val_max); + subfield_nomore_end(cursor, max, val, val_max); break; } case WALLY_PSBT_IN_REDEEM_SCRIPT: { - if (result->redeem_script_len != 0) { + if (result->redeem_script) { return WALLY_EINVAL; /* Already have a redeem script */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Type is more than one byte */ } - p += varint_from_bytes(p, &value_len); - clone_bytes(&result->redeem_script, p, value_len); - result->redeem_script_len = value_len; + subfield_nomore_end(cursor, max, key, key_len); - p += value_len; + if (!clone_varlength(&result->redeem_script, + &result->redeem_script_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->redeem_script_len == 0) { + result->redeem_script = wally_malloc(1); + } break; } case WALLY_PSBT_IN_WITNESS_SCRIPT: { - if (result->witness_script_len != 0) { + if (result->witness_script) { return WALLY_EINVAL; /* Already have a witness script */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Type is more than one byte */ } - p += varint_from_bytes(p, &value_len); - clone_bytes(&result->witness_script, p, value_len); - result->witness_script_len = value_len; + subfield_nomore_end(cursor, max, key, key_len); - p += value_len; + if (!clone_varlength(&result->witness_script, + &result->witness_script_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->witness_script_len == 0) { + result->witness_script = wally_malloc(1); + } break; } case WALLY_PSBT_IN_BIP32_DERIVATION: { - struct wally_keypath_map *keypaths = result->keypaths; - size_t path_len; - if (key_len != 66 && key_len != 34) { - return WALLY_EINVAL; /* Size of key is unexpected */ - } - /* Check for duplicates */ - for (i = 0; i < keypaths->num_items; ++i) { - if (memcmp(keypaths->items[i].pubkey, &key[1], key_len - 1) == 0) { - return WALLY_EINVAL; /* Duplicate key */ - } - } - - memcpy(keypaths->items[keypaths->num_items].pubkey, &key[1], key_len - 1); - - /* Read the path length */ - p += varint_from_bytes(p, &value_len); - if (value_len % 4 != 0 || value_len == 0) { - return WALLY_EINVAL; /* Invalid length for keypaths */ - } - path_len = (value_len / 4) - 1; - - /* Read the fingerprint */ - memcpy(keypaths->items[keypaths->num_items].origin.fingerprint, p, 4); - p += 4; - - /* Read the path itself */ - keypaths->items[keypaths->num_items].origin.path = wally_malloc(path_len * sizeof(uint32_t)); - for (i = 0; i < path_len; ++i) { - p += uint32_from_le_bytes(p, &keypaths->items[keypaths->num_items].origin.path[i]); + ret = pull_keypath(cursor, max, key, key_len, result->keypaths); + if (ret != WALLY_OK) { + return ret; } - keypaths->items[keypaths->num_items].origin.path_len = path_len; - - keypaths->num_items++; break; } case WALLY_PSBT_IN_FINAL_SCRIPTSIG: { - if (result->final_script_sig_len != 0) { + if (result->final_script_sig) { return WALLY_EINVAL; /* Already have a scriptSig */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Type is more than one byte */ } - p += varint_from_bytes(p, &value_len); - clone_bytes(&result->final_script_sig, p, value_len); - result->final_script_sig_len = value_len; + subfield_nomore_end(cursor, max, key, key_len); - p += value_len; + if (!clone_varlength(&result->final_script_sig, + &result->final_script_sig_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->final_script_sig_len == 0) { + result->final_script_sig = wally_malloc(1); + } break; } case WALLY_PSBT_IN_FINAL_SCRIPTWITNESS: { uint64_t num_witnesses; + size_t i; if (result->final_witness) { return WALLY_EINVAL; /* Already have a scriptWitness */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Type is more than one byte */ } - p += varint_from_bytes(p, &value_len); - p += varint_from_bytes(p, &num_witnesses); + subfield_nomore_end(cursor, max, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); + num_witnesses = pull_varint(&val, &val_max); ret = wally_tx_witness_stack_init_alloc(num_witnesses, &result->final_witness); if (ret != WALLY_OK) { return ret; } for (i = 0; i < num_witnesses; ++i) { - uint64_t witness_len; - p += varint_from_bytes(p, &witness_len); - ret = wally_tx_witness_stack_set(result->final_witness, i, p, witness_len); - if (ret != WALLY_OK) + uint64_t witness_len = pull_varint(&val, &val_max); + ret = wally_tx_witness_stack_set(result->final_witness, i, + pull_skip(&val, &val_max, witness_len), + witness_len); + if (ret != WALLY_OK) { return ret; - p += witness_len; + } } + subfield_nomore_end(cursor, max, val, val_max); break; } - /* Unknowns */ - default: { - struct wally_unknowns_map *unknowns = result->unknowns; - clone_bytes(&unknowns->items[unknowns->num_items].key, key, key_len); - unknowns->items[unknowns->num_items].key_len = key_len; + case WALLY_PSBT_PROPRIETARY_TYPE: { +#ifdef BUILD_ELEMENTS + uint64_t id_len; + bool valid_type = false; + + id_len = pull_varlength(&key, &key_len); + if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { + /* Skip the elements_id prefix */ + pull_skip(&key, &key_len, WALLY_ELEMENTS_ID_LEN); + + switch (pull_varint(&key, &key_len)) { + case WALLY_PSBT_IN_ELEMENTS_VALUE: { + valid_type = true; + if (result->has_value) { + return WALLY_EINVAL; /* Already have value */ + } + subfield_nomore_end(cursor, max, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); + result->value = pull_le64(&val, &val_max); + subfield_nomore_end(cursor, max, val, val_max); + result->has_value = true; + break; + } + case WALLY_PSBT_IN_ELEMENTS_VALUE_BLINDER: { + valid_type = true; + if (result->value_blinder) { + return WALLY_EINVAL; /* Already have value blinding factor */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->value_blinder, + &result->value_blinder_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->value_blinder_len == 0) { + result->value_blinder = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_ASSET: { + valid_type = true; + if (result->asset) { + return WALLY_EINVAL; /* Already have asset */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->asset, + &result->asset_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->asset_len == 0) { + result->asset = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_ASSET_BLINDER: { + valid_type = true; + if (result->asset_blinder) { + return WALLY_EINVAL; /* Already have asset blinding factor */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->asset_blinder, + &result->asset_blinder_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->asset_blinder_len == 0) { + result->asset_blinder = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX: { + valid_type = true; + if (result->peg_in_tx) { + return WALLY_EINVAL; /* Already have asset */ + } + subfield_nomore_end(cursor, max, key, key_len); - p += varint_from_bytes(p, &value_len); - value = p; - clone_bytes(&unknowns->items[unknowns->num_items].value, value, value_len); - unknowns->items[unknowns->num_items].value_len = value_len; + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); - unknowns->num_items++; - p += value_len; + ret = wally_tx_from_bytes(val, val_max, WALLY_TX_FLAG_USE_ELEMENTS, + &result->peg_in_tx); + if (ret != WALLY_OK) { + return ret; + } + pull_subfield_end(cursor, max, val, val_max); + break; + } + case WALLY_PSBT_IN_ELEMENTS_TXOUT_PROOF: { + valid_type = true; + if (result->txout_proof) { + return WALLY_EINVAL; /* Already have txout proof */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->txout_proof, + &result->txout_proof_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->txout_proof_len == 0) { + result->txout_proof = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_GENESIS_HASH: { + valid_type = true; + if (result->genesis_hash) { + return WALLY_EINVAL; /* Already have genesis hash */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->genesis_hash, + &result->genesis_hash_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->genesis_hash_len == 0) { + result->genesis_hash = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_CLAIM_SCRIPT: { + valid_type = true; + if (result->claim_script) { + return WALLY_EINVAL; /* Already have asset */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->claim_script, + &result->claim_script_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->claim_script_len == 0) { + result->claim_script = wally_malloc(1); + } + break; + } + default: + valid_type = false; + } + } + if (valid_type) { + break; + } +#endif /* BUILD_ELEMENTS */ + } + /* For unknown case without elements or for unknown proprietary types */ + /* fall through */ + /* Unknowns */ + default: { + ret = pull_unknown_key_value(cursor, max, pre_key, result->unknowns); + if (ret != WALLY_OK) { + return ret; + } break; } } - } + pre_key = *cursor; - if (!found_sep) { - return WALLY_EINVAL; } - *bytes_read = p - bytes; -fail: - return ret; + return WALLY_OK; } -static int psbt_output_from_bytes( - const unsigned char *bytes, - size_t bytes_len, +static int pull_psbt_output( + const unsigned char **cursor, + size_t *max, struct psbt_output_counts counts, - size_t *bytes_read, struct wally_psbt_output *result) { - const unsigned char *p = bytes, *end = bytes + bytes_len, *key, *value; - uint64_t key_len, value_len; - uint8_t type; - size_t i, vl; - bool found_sep = false; - int ret = WALLY_OK; + int ret; + size_t key_len; + const unsigned char *pre_key; /* Init and alloc the maps */ if (counts.num_keypaths > 0) { @@ -1312,116 +2190,231 @@ static int psbt_output_from_bytes( wally_unknowns_map_init_alloc(counts.num_unknowns, &result->unknowns); } - /* Read key value pairs */ - while (p < end) { - /* Read the key length */ - vl = varint_from_bytes(p, &key_len); - CHECK_BUF_BOUNDS(p, key_len + vl, bytes, bytes_len) - p += vl; - - if (key_len == 0) { - found_sep = true; - break; - } - - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; + /* Read key value */ + pre_key = *cursor; + while ((key_len = pull_varlength(cursor, max)) != 0) { + const unsigned char *key; - /* Pre-read the value length but don't increment for a sanity check */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) + /* Start parsing key */ + pull_subfield_start(cursor, max, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_OUT_REDEEM_SCRIPT: { - if (result->redeem_script_len != 0) { + if (result->redeem_script) { return WALLY_EINVAL; /* Already have a redeem script */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Type is more than one byte */ } - p += varint_from_bytes(p, &value_len); - clone_bytes(&result->redeem_script, p, value_len); - result->redeem_script_len = value_len; + subfield_nomore_end(cursor, max, key, key_len); - p += value_len; + if (!clone_varlength(&result->redeem_script, + &result->redeem_script_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->redeem_script_len == 0) { + result->redeem_script = wally_malloc(1); + } break; } case WALLY_PSBT_OUT_WITNESS_SCRIPT: { - if (result->witness_script_len != 0) { + if (result->witness_script) { return WALLY_EINVAL; /* Already have a witness script */ - } else if (key_len != 1) { - return WALLY_EINVAL; /* Type is more than one byte */ } - p += varint_from_bytes(p, &value_len); - clone_bytes(&result->witness_script, p, value_len); - result->witness_script_len = value_len; + subfield_nomore_end(cursor, max, key, key_len); - p += value_len; + if (!clone_varlength(&result->witness_script, + &result->witness_script_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->witness_script_len == 0) { + result->witness_script = wally_malloc(1); + } break; } case WALLY_PSBT_OUT_BIP32_DERIVATION: { - struct wally_keypath_map *keypaths = result->keypaths; - size_t path_len; - if (key_len != 66 && key_len != 34) { - return WALLY_EINVAL; /* Size of key is unexpected */ + ret = pull_keypath(cursor, max, key, key_len, result->keypaths); + if (ret != WALLY_OK) { + return ret; } - /* Check for duplicates */ - for (i = 0; i < keypaths->num_items; ++i) { - if (memcmp(keypaths->items[i].pubkey, &key[1], key_len - 1) == 0) { - return WALLY_EINVAL; /* Duplicate key */ + break; + } + case WALLY_PSBT_PROPRIETARY_TYPE: { +#ifdef BUILD_ELEMENTS + uint64_t id_len; + bool valid_type = false; + + id_len = pull_varlength(&key, &key_len); + if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { + /* Skip the elements_id prefix */ + pull_skip(&key, &key_len, WALLY_ELEMENTS_ID_LEN); + + switch (pull_varint(&key, &key_len)) { + case WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT: { + valid_type = true; + if (result->value_commitment) { + return WALLY_EINVAL; /* Already have value commitment */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->value_commitment, + &result->value_commitment_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->value_commitment_len == 0) { + result->value_commitment = wally_malloc(1); + } + break; } - } + case WALLY_PSBT_OUT_ELEMENTS_VALUE_BLINDER: { + valid_type = true; + if (result->value_blinder) { + return WALLY_EINVAL; /* Already have value blinder */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->value_blinder, + &result->value_blinder_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->value_blinder_len == 0) { + result->value_blinder = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_ASSET_COMMITMENT: { + valid_type = true; + if (result->asset_commitment) { + return WALLY_EINVAL; /* Already have asset commitment */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->asset_commitment, + &result->asset_commitment_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->asset_commitment_len == 0) { + result->asset_commitment = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_ASSET_BLINDER: { + valid_type = true; + if (result->asset_blinder) { + return WALLY_EINVAL; /* Already have asset blinder */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->asset_blinder, + &result->asset_blinder_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->asset_blinder_len == 0) { + result->asset_blinder = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_RANGE_PROOF: { + valid_type = true; + if (result->range_proof) { + return WALLY_EINVAL; /* Already have range proof */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->range_proof, + &result->range_proof_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->range_proof_len == 0) { + result->range_proof = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_SURJECTION_PROOF: { + valid_type = true; + if (result->surjection_proof) { + return WALLY_EINVAL; /* Already have surjection proof */ + } + subfield_nomore_end(cursor, max, key, key_len); + + if (!clone_varlength(&result->surjection_proof, + &result->surjection_proof_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->surjection_proof_len == 0) { + result->surjection_proof = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_BLINDING_PUBKEY: { + const unsigned char *val; + size_t val_max; + valid_type = true; + if (result->has_blinding_pubkey) { + return WALLY_EINVAL; /* Already have blinding pubkey */ + } + subfield_nomore_end(cursor, max, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); + if (val_max != EC_PUBLIC_KEY_UNCOMPRESSED_LEN && val_max != EC_PUBLIC_KEY_LEN) { + return WALLY_EINVAL; /* Size of key is unexpected */ + } + memcpy(result->blinding_pubkey, val, val_max); + result->has_blinding_pubkey = true; - memcpy(keypaths->items[keypaths->num_items].pubkey, &key[1], key_len - 1); + pull_subfield_end(cursor, max, val, val_max); + break; + } + case WALLY_PSBT_OUT_ELEMENTS_NONCE_COMMITMENT: { + valid_type = true; + if (result->nonce_commitment) { + return WALLY_EINVAL; /* Already have nonce commitment */ + } + subfield_nomore_end(cursor, max, key, key_len); - /* Read the path length */ - p += varint_from_bytes(p, &value_len); - if (value_len % 4 != 0 || value_len == 0) { - return WALLY_EINVAL; /* Invalid length for keypaths */ + if (!clone_varlength(&result->nonce_commitment, + &result->nonce_commitment_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->nonce_commitment_len == 0) { + result->nonce_commitment = wally_malloc(1); + } + break; + } + default: + valid_type = false; + } } - path_len = (value_len / 4) - 1; - - /* Read the fingerprint */ - memcpy(keypaths->items[keypaths->num_items].origin.fingerprint, p, 4); - p += 4; - - /* Read the path itself */ - keypaths->items[keypaths->num_items].origin.path = wally_malloc(path_len * sizeof(uint32_t)); - for (i = 0; i < path_len; ++i) { - p += uint32_from_le_bytes(p, &keypaths->items[keypaths->num_items].origin.path[i]); + if (valid_type) { + break; } - keypaths->items[keypaths->num_items].origin.path_len = path_len; - - keypaths->num_items++; - break; +#endif /* BUILD_ELEMENTS */ } + /* For unknown case without elements or for unknown proprietary types */ + /* fall through */ /* Unknowns */ default: { - struct wally_unknowns_map *unknowns = result->unknowns; - clone_bytes(&unknowns->items[unknowns->num_items].key, key, key_len); - unknowns->items[unknowns->num_items].key_len = key_len; - - p += varint_from_bytes(p, &value_len); - value = p; - clone_bytes(&unknowns->items[unknowns->num_items].value, value, value_len); - unknowns->items[unknowns->num_items].value_len = value_len; - - unknowns->num_items++; - p += value_len; + ret = pull_unknown_key_value(cursor, max, pre_key, result->unknowns); + if (ret != WALLY_OK) { + return ret; + } break; } } + pre_key = *cursor; } - if (!found_sep) { - return WALLY_EINVAL; - } - - *bytes_read = p - bytes; -fail: - return ret; + return WALLY_OK; } int wally_psbt_from_bytes( @@ -1429,30 +2422,35 @@ int wally_psbt_from_bytes( size_t bytes_len, struct wally_psbt **output) { - const unsigned char *p = bytes, *end = bytes + bytes_len, *key, *value; - uint64_t key_len, value_len; - uint8_t type; - size_t i, vl; - int ret = WALLY_OK; + const unsigned char *magic, *pre_key; + int ret; + size_t i, key_len; struct psbt_counts *counts = NULL; struct wally_psbt *result = NULL; - bool found_sep; + bool is_elements = false; TX_CHECK_OUTPUT; - /* Check the magic */ - if (bytes_len <= 5) { + magic = pull_skip(&bytes, &bytes_len, sizeof(WALLY_PSBT_MAGIC)); + if (!magic) { ret = WALLY_EINVAL; /* Not enough bytes */ goto fail; } - if (memcmp(p, WALLY_PSBT_MAGIC, 5) != 0 ) { + if (memcmp(magic, WALLY_PSBT_MAGIC, sizeof(WALLY_PSBT_MAGIC)) != 0 ) { +#ifdef BUILD_ELEMENTS + if (memcmp(magic, WALLY_ELEMENTS_PSBT_MAGIC, sizeof(WALLY_ELEMENTS_PSBT_MAGIC)) != 0) { + ret = WALLY_EINVAL; /* Invalid Magic */ + goto fail; + } + is_elements = true; +#else ret = WALLY_EINVAL; /* Invalid Magic */ goto fail; +#endif /* BUILD_ELEMENTS */ } - p += 5; /* Get a count of the psbt parts */ - if (count_psbt_parts(bytes, bytes_len, &counts) != WALLY_OK) { + if (count_psbt_parts(bytes, bytes_len, is_elements, &counts) != WALLY_OK) { ret = WALLY_EINVAL; goto fail; } @@ -1464,73 +2462,84 @@ int wally_psbt_from_bytes( } *output = result; - /* Read globals first */ - found_sep = false; - while (p < end) { - /* Read the key length */ - vl = varint_from_bytes(p, &key_len); - CHECK_BUF_BOUNDS(p, key_len + vl, bytes, bytes_len) - p += vl; - - if (key_len == 0) { - found_sep = true; - break; - } + /* Set the magic */ + memcpy(result->magic, magic, 5); - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; + /* Read globals first */ + pre_key = bytes; + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { + const unsigned char *key, *val; + size_t val_max; - /* Pre-read the value length but don't increment for a sanity check */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) + /* Start parsing key */ + pull_subfield_start(&bytes, &bytes_len, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_GLOBAL_UNSIGNED_TX: { - size_t j; + int flags = 0; if (result->tx) { ret = WALLY_EINVAL; /* We already have a global tx */ goto fail; - } else if (key_len != 1) { - ret = WALLY_EINVAL; /* Global tx key is one byte type */ - goto fail; } - p += varint_from_bytes(p, &value_len); - value = p; - if ((ret = wally_tx_from_bytes(value, value_len, 0, &result->tx)) != WALLY_OK) { + subfield_nomore_end(&bytes, &bytes_len, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(&bytes, &bytes_len, + pull_varint(&bytes, &bytes_len), + &val, &val_max); + if (is_elements) { + flags |= WALLY_TX_FLAG_USE_ELEMENTS; + } + ret = wally_tx_from_bytes(val, val_max, flags, &result->tx); + if (ret != WALLY_OK) { goto fail; } - p += value_len; + pull_subfield_end(&bytes, &bytes_len, val, val_max); + /* Make sure there are no scriptSigs and scriptWitnesses */ - for (j = 0; j < result->tx->num_inputs; ++j) { - if (result->tx->inputs[j].script_len != 0 || (result->tx->inputs[j].witness && result->tx->inputs[j].witness->num_items != 0)) { + for (i = 0; i < result->tx->num_inputs; ++i) { + if (result->tx->inputs[i].script_len != 0 || (result->tx->inputs[i].witness && result->tx->inputs[i].witness->num_items != 0)) { ret = WALLY_EINVAL; /* Unsigned tx needs empty scriptSigs and scriptWtinesses */ goto fail; } } break; } + case WALLY_PSBT_GLOBAL_VERSION: { + if (result->version > 0) { + ret = WALLY_EINVAL; /* Version already provided */ + goto fail; + } + subfield_nomore_end(&bytes, &bytes_len, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(&bytes, &bytes_len, + pull_varint(&bytes, &bytes_len), + &val, &val_max); + result->version = pull_le32(&val, &val_max); + subfield_nomore_end(&bytes, &bytes_len, val, val_max); + if (result->version > WALLY_PSBT_HIGHEST_VERSION) { + ret = WALLY_EINVAL; /* Unsupported version number */ + goto fail; + } + break; + } /* Unknowns */ default: { - struct wally_unknowns_map *unknowns = result->unknowns; - clone_bytes(&unknowns->items[unknowns->num_items].key, key, key_len); - unknowns->items[unknowns->num_items].key_len = key_len; - - p += varint_from_bytes(p, &value_len); - value = p; - clone_bytes(&unknowns->items[unknowns->num_items].value, value, value_len); - unknowns->items[unknowns->num_items].value_len = value_len; - - unknowns->num_items++; - p += value_len; + ret = pull_unknown_key_value(&bytes, &bytes_len, pre_key, + result->unknowns); + if (ret != WALLY_OK) { + return ret; + } break; } } + pre_key = bytes; } - if (!found_sep) { + /* We don't technically need to test here, but it's a minor optimization */ + if (!bytes) { ret = WALLY_EINVAL; /* Missing global separator */ goto fail; } @@ -1541,15 +2550,14 @@ int wally_psbt_from_bytes( } /* Read inputs */ - for (i = 0; i < counts->num_inputs && p < end; ++i) { - size_t bytes_read; - - ret = psbt_input_from_bytes(p, end - p, counts->input_counts[i], &bytes_read, &result->inputs[i]); + for (i = 0; i < counts->num_inputs; ++i) { + ret = pull_psbt_input(&bytes, &bytes_len, counts->input_counts[i], is_elements, + &result->inputs[i]); + /* Increment this now, might be partially initialized! */ + result->num_inputs++; if (ret != WALLY_OK) { goto fail; } - p += bytes_read; - result->num_inputs++; } /* Make sure that the number of inputs matches the number of inputs in the transaction */ @@ -1559,19 +2567,17 @@ int wally_psbt_from_bytes( } /* Read outputs */ - for (i = 0; i < counts->num_outputs && p < end; ++i) { - size_t bytes_read; - - ret = psbt_output_from_bytes(p, end - p, counts->output_counts[i], &bytes_read, &result->outputs[i]); + for (i = 0; i < counts->num_outputs; ++i) { + ret = pull_psbt_output(&bytes, &bytes_len, counts->output_counts[i], + &result->outputs[i]); + result->num_outputs++; if (ret != WALLY_OK) { goto fail; } - p += bytes_read; - result->num_outputs++; } - /* Make sure that the number of outputs matches the number ot outputs in the transaction */ - if (result->num_outputs != result->tx->num_outputs) { + /* If we ran out of data anywhere, fail. */ + if (bytes == NULL) { ret = WALLY_EINVAL; goto fail; } @@ -1586,460 +2592,372 @@ int wally_psbt_from_bytes( return ret; } -static int psbt_input_get_length( - const struct wally_psbt_input *input, +int wally_psbt_get_length( + const struct wally_psbt *psbt, size_t *len) { int ret; - size_t out, tx_len, i; - if (!len) { - return WALLY_EINVAL; - } - - *len = 0; - out = 0; - - /* Non witness utxo */ - if (input->non_witness_utxo) { - out += 2; /* Key len and one byte type */ - ret = wally_tx_get_length(input->non_witness_utxo, WALLY_TX_FLAG_USE_WITNESS, &tx_len); - if (ret != WALLY_OK) { - return ret; - } - out += varbuff_get_length(tx_len); - } - /* Witness utxo */ - if (input->witness_utxo) { - size_t wit_size = 0; - out += 2; /* Key len and one byte type */ - wit_size += sizeof(input->witness_utxo->satoshi); - wit_size += varbuff_get_length(input->witness_utxo->script_len); - out += varbuff_get_length(wit_size); - } - /* Partial sigs */ - if (input->partial_sigs) { - struct wally_partial_sigs_map *partial_sigs = input->partial_sigs; - for (i = 0; i < partial_sigs->num_items; ++i) { - struct wally_partial_sigs_item *item = &partial_sigs->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - out += varint_get_length(34); - out += 34; /* Compressed pubkey + 1 byte type */ - } else { - out += varint_get_length(66); - out += 66; /* Uncompressed pubkey + 1 byte type */ - } - out += varbuff_get_length(item->sig_len); - } - } - /* Sighash type */ - if (input->sighash_type > 0) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(sizeof(input->sighash_type)); - } - /* Redeem script */ - if (input->redeem_script) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(input->redeem_script_len); - } - /* Witness script */ - if (input->witness_script) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(input->witness_script_len); - } - /* Keypaths */ - if (input->keypaths) { - struct wally_keypath_map *keypaths = input->keypaths; - for (i = 0; i < keypaths->num_items; ++i) { - size_t origin_len; - struct wally_keypath_item *item = &keypaths->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - out += varint_get_length(34); - out += 34; /* Compressed pubkey + 1 byte type */ - } else { - out += varint_get_length(66); - out += 66; /* Uncompressed pubkey + 1 byte type */ - } - origin_len = 4; /* Start with 4 bytes for fingerprint */ - origin_len += item->origin.path_len * sizeof(uint32_t); - out += varint_get_length(origin_len); - out += origin_len; - } - } - /* Final scriptSig */ - if (input->final_script_sig) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(input->final_script_sig_len); - } - /* Final scriptWitness */ - if (input->final_witness) { - struct wally_tx_witness_stack *witness = input->final_witness; - size_t wit_len = varint_get_length(witness->num_items); - out += 2; /* Key len and one byte type */ - out += varint_get_length(witness->num_items); - for (i = 0; i < witness->num_items; ++i) { - out += varbuff_get_length(witness->items[i].witness_len); - wit_len += varbuff_get_length(witness->items[i].witness_len); - } - out += varint_get_length(wit_len); + ret = wally_psbt_to_bytes(psbt, NULL, 0, len); + if (ret == WALLY_EINVAL && *len != 0) { + return WALLY_OK; } - /* Unknowns */ - if (input->unknowns) { - for (i = 0; i < input->unknowns->num_items; ++i) { - struct wally_unknowns_item *unknown = &input->unknowns->items[i]; - out += varbuff_get_length(unknown->key_len); - out += varbuff_get_length(unknown->value_len); - } - } - - /* Separator */ - out += 1; - - *len = out; - return WALLY_OK; + return ret; } -static int psbt_output_get_length( - const struct wally_psbt_output *output, - size_t *len) +/* Literally a varbuff containing only type as a varint, then optional data */ +static void push_psbt_key( + unsigned char **cursor, size_t *max, + uint64_t type, const void *extra, size_t extra_len) { - size_t out, i; - if (!len) { - return WALLY_EINVAL; - } - - *len = 0; - out = 0; - - /* Redeem script */ - if (output->redeem_script) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(output->redeem_script_len); - } - /* Witness script */ - if (output->witness_script) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(output->witness_script_len); - } - /* Keypaths */ - if (output->keypaths) { - struct wally_keypath_map *keypaths = output->keypaths; - for (i = 0; i < keypaths->num_items; ++i) { - size_t origin_len; - struct wally_keypath_item *item = &keypaths->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - out += varint_get_length(34); - out += 34; /* Compressed pubkey + 1 byte type */ - } else { - out += varint_get_length(66); - out += 66; /* Uncompressed pubkey + 1 byte type */ - } + push_varint(cursor, max, varint_get_length(type) + extra_len); + push_varint(cursor, max, type); + push_bytes(cursor, max, extra, extra_len); +} - origin_len = 4; /* Start with 4 bytes for fingerprint */ - origin_len += item->origin.path_len * sizeof(uint32_t); - out += varint_get_length(origin_len); - out += origin_len; - } - } - /* Unknowns */ - if (output->unknowns) { - for (i = 0; i < output->unknowns->num_items; ++i) { - struct wally_unknowns_item *unknown = &output->unknowns->items[i]; - out += varbuff_get_length(unknown->key_len); - out += varbuff_get_length(unknown->value_len); - } +/* Common case of pushing a type whose key is a pubkey */ +static void push_psbt_key_with_pubkey( + unsigned char **cursor, size_t *max, + uint64_t type, + const unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]) +{ + if (pubkey_is_compressed(pubkey)) { + push_psbt_key(cursor, max, type, pubkey, EC_PUBLIC_KEY_LEN); + } else { + push_psbt_key(cursor, max, type, pubkey, + EC_PUBLIC_KEY_UNCOMPRESSED_LEN); } +} - /* Separator */ - out += 1; - - *len = out; - return WALLY_OK; +#ifdef BUILD_ELEMENTS +/* Common case of pushing elements proprietary type keys */ +static void push_psbt_elements_key( + unsigned char **cursor, size_t *max, + uint64_t type, const void *extra, size_t extra_len) +{ + push_varint(cursor, max, varint_get_length(WALLY_PSBT_PROPRIETARY_TYPE) + + varint_get_length(WALLY_ELEMENTS_ID_LEN) + + WALLY_ELEMENTS_ID_LEN + varint_get_length(type) + extra_len); + push_varint(cursor, max, WALLY_PSBT_PROPRIETARY_TYPE); + push_varbuff(cursor, max, WALLY_ELEMENTS_ID, WALLY_ELEMENTS_ID_LEN); + push_varint(cursor, max, type); + push_bytes(cursor, max, extra, extra_len); } +#endif /* BUILD_ELEMENTS */ -int wally_psbt_get_length( - const struct wally_psbt *psbt, - size_t *len) +static int push_length_and_tx( + unsigned char **cursor, size_t *max, + const struct wally_tx *tx, uint32_t flags, + bool partial_ok) { int ret; - size_t out, tx_len, i; - if (!len) { - return WALLY_EINVAL; - } - - *len = 0; - out = 5; /* Start with 5 byte magic */ + size_t txlen; + unsigned char *p; - /* Global tx */ - out += 2; - ret = wally_tx_get_length(psbt->tx, 0, &tx_len); + ret = wally_tx_get_length(tx, flags, &txlen); if (ret != WALLY_OK) { return ret; } - out += varbuff_get_length(tx_len); - /* Global unknowns */ - if (psbt->unknowns) { - for (i = 0; i < psbt->unknowns->num_items; ++i) { - struct wally_unknowns_item *unknown = &psbt->unknowns->items[i]; - out += varbuff_get_length(unknown->key_len); - out += varbuff_get_length(unknown->value_len); - } + push_varint(cursor, max, txlen); + + /* FIXME: convert wally_tx to use push */ + p = push_bytes(cursor, max, NULL, txlen); + if (!p) { + /* We catch this in caller. */ + return WALLY_OK; } - /* Separator */ - out += 1; + return wally_partial_tx_to_bytes(tx, flags, p, txlen, partial_ok, &txlen); +} - /* Get lengths of each input and output */ - for (i = 0; i < psbt->num_inputs; ++i) { - struct wally_psbt_input *input = &psbt->inputs[i]; - size_t input_len; - psbt_input_get_length(input, &input_len); - out += input_len; - } - for (i = 0; i < psbt->num_outputs; ++i) { - struct wally_psbt_output *output = &psbt->outputs[i]; - size_t output_len; - psbt_output_get_length(output, &output_len); - out += output_len; +static void push_witness_stack( + unsigned char **cursor, size_t *max, + const struct wally_tx_witness_stack *witness) +{ + size_t i; + + push_varint(cursor, max, witness->num_items); + for (i = 0; i < witness->num_items; ++i) { + push_varbuff(cursor, max, witness->items[i].witness, + witness->items[i].witness_len); } +} - *len = out; - return WALLY_OK; +static void push_keypath_item( + unsigned char **cursor, size_t *max, + uint64_t type, + const struct wally_keypath_item *item) +{ + size_t origin_len, i; + + push_psbt_key_with_pubkey(cursor, max, type, item->pubkey); + + origin_len = 4; /* Start with 4 bytes for fingerprint */ + origin_len += item->origin.path_len * sizeof(uint32_t); + push_varint(cursor, max, origin_len); + + push_bytes(cursor, max, item->origin.fingerprint, 4); + for (i = 0; i < item->origin.path_len; ++i) { + push_bytes(cursor, max, + &item->origin.path[i], sizeof(uint32_t)); + } } -static int psbt_input_to_bytes( - const struct wally_psbt_input *input, - unsigned char *bytes_out, size_t len, - size_t *bytes_written) +static int push_psbt_input( + unsigned char **cursor, size_t *max, + bool is_elements, + const struct wally_psbt_input *input) { - unsigned char type, *p = bytes_out, *end = bytes_out + len; int ret; - size_t i, tx_len; + size_t i; /* Non witness utxo */ if (input->non_witness_utxo) { - type = WALLY_PSBT_IN_NON_WITNESS_UTXO; - p += varbuff_to_bytes(&type, 1, p); - ret = wally_tx_get_length(input->non_witness_utxo, WALLY_TX_FLAG_USE_WITNESS, &tx_len); - if (ret != WALLY_OK) { - return ret; - } - p += varint_to_bytes(tx_len, p); - ret = wally_tx_to_bytes(input->non_witness_utxo, WALLY_TX_FLAG_USE_WITNESS, p, end - p, &tx_len); + push_psbt_key(cursor, max, WALLY_PSBT_IN_NON_WITNESS_UTXO, NULL, 0); + ret = push_length_and_tx(cursor, max, + input->non_witness_utxo, + WALLY_TX_FLAG_USE_WITNESS, false); if (ret != WALLY_OK) { return ret; } - p += tx_len; } + /* Witness utxo */ +#ifdef BUILD_ELEMENTS + if (is_elements && input->witness_utxo) { + struct wally_tx_output *utxo = input->witness_utxo; + size_t buf_len = push_elements_bytes_size(utxo); + unsigned char buf[buf_len], *ptr = buf; + /* Push the asset, value, nonce, then scriptpubkey */ + push_psbt_key(cursor, max, WALLY_PSBT_IN_WITNESS_UTXO, NULL, 0); + + push_elements_bytes(&ptr, &buf_len, + utxo->asset, + utxo->asset_len); + push_elements_bytes(&ptr, &buf_len, + utxo->value, + utxo->value_len); + push_elements_bytes(&ptr, &buf_len, + utxo->nonce, + utxo->nonce_len); + push_varbuff(&ptr, &buf_len, + utxo->script, + utxo->script_len); + + assert(buf_len == 0); + push_varbuff(cursor, max, buf, sizeof(buf)); + } else +#else + if (is_elements) { + return WALLY_ERROR; + } +#endif /* BUILD_ELEMENTS */ if (input->witness_utxo) { unsigned char wit_bytes[50], *w = wit_bytes; /* Witness outputs can be no larger than 50 bytes as specified in BIP 141 */ - size_t wit_len; - type = WALLY_PSBT_IN_WITNESS_UTXO; - p += varbuff_to_bytes(&type, 1, p); + size_t wit_max = sizeof(wit_bytes); + + push_psbt_key(cursor, max, WALLY_PSBT_IN_WITNESS_UTXO, NULL, 0); - /* Serialize the output to the temp buffer; */ - w += uint64_to_le_bytes(input->witness_utxo->satoshi, w); - w += varbuff_to_bytes(input->witness_utxo->script, input->witness_utxo->script_len, w); - wit_len = w - wit_bytes; + push_le64(&w, &wit_max, input->witness_utxo->satoshi); + push_varbuff(&w, &wit_max, + input->witness_utxo->script, + input->witness_utxo->script_len); - p += varint_to_bytes(wit_len, p); - memcpy(p, wit_bytes, wit_len); - p += wit_len; + if (!w) { + return WALLY_EINVAL; + } + push_varbuff(cursor, max, wit_bytes, w - wit_bytes); } /* Partial sigs */ if (input->partial_sigs) { struct wally_partial_sigs_map *partial_sigs = input->partial_sigs; for (i = 0; i < partial_sigs->num_items; ++i) { struct wally_partial_sigs_item *item = &partial_sigs->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - p += varint_to_bytes(34, p); - *p = WALLY_PSBT_IN_PARTIAL_SIG; - p++; - memcpy(p, item->pubkey, EC_PUBLIC_KEY_LEN); - p += EC_PUBLIC_KEY_LEN; - } else { - p += varint_to_bytes(66, p); - *p = WALLY_PSBT_IN_PARTIAL_SIG; - p++; - memcpy(p, item->pubkey, EC_PUBLIC_KEY_UNCOMPRESSED_LEN); - p += EC_PUBLIC_KEY_UNCOMPRESSED_LEN; - } - p += varbuff_to_bytes(item->sig, item->sig_len, p); + push_psbt_key_with_pubkey(cursor, max, WALLY_PSBT_IN_PARTIAL_SIG, + item->pubkey); + push_varbuff(cursor, max, item->sig, item->sig_len); } } /* Sighash type */ if (input->sighash_type > 0) { - type = WALLY_PSBT_IN_SIGHASH_TYPE; - p += varbuff_to_bytes(&type, 1, p); - p += varint_to_bytes(sizeof(uint32_t), p); - p += uint32_to_le_bytes(input->sighash_type, p); + push_psbt_key(cursor, max, WALLY_PSBT_IN_SIGHASH_TYPE, NULL, 0); + push_varint(cursor, max, sizeof(uint32_t)); + push_le32(cursor, max, input->sighash_type); } /* Redeem script */ if (input->redeem_script) { - type = WALLY_PSBT_IN_REDEEM_SCRIPT; - p += varbuff_to_bytes(&type, 1, p); - p += varbuff_to_bytes(input->redeem_script, input->redeem_script_len, p); + push_psbt_key(cursor, max, WALLY_PSBT_IN_REDEEM_SCRIPT, NULL, 0); + push_varbuff(cursor, max, + input->redeem_script, input->redeem_script_len); } /* Witness script */ if (input->witness_script) { - type = WALLY_PSBT_IN_WITNESS_SCRIPT; - p += varbuff_to_bytes(&type, 1, p); - p += varbuff_to_bytes(input->witness_script, input->witness_script_len, p); + push_psbt_key(cursor, max, WALLY_PSBT_IN_WITNESS_SCRIPT, NULL, 0); + push_varbuff(cursor, max, + input->witness_script, input->witness_script_len); } /* Keypaths */ if (input->keypaths) { struct wally_keypath_map *keypaths = input->keypaths; for (i = 0; i < keypaths->num_items; ++i) { - size_t origin_len, j; - struct wally_keypath_item *item = &keypaths->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - p += varint_to_bytes(34, p); - *p = WALLY_PSBT_IN_BIP32_DERIVATION; - p++; - memcpy(p, item->pubkey, EC_PUBLIC_KEY_LEN); - p += EC_PUBLIC_KEY_LEN; - } else { - p += varint_to_bytes(66, p); - *p = WALLY_PSBT_IN_BIP32_DERIVATION; - p++; - memcpy(p, item->pubkey, EC_PUBLIC_KEY_UNCOMPRESSED_LEN); - p += EC_PUBLIC_KEY_UNCOMPRESSED_LEN; - } - - origin_len = 4; /* Start with 4 bytes for fingerprint */ - origin_len += item->origin.path_len * sizeof(uint32_t); - p += varint_to_bytes(origin_len, p); - - memcpy(p, item->origin.fingerprint, 4); - p += 4; - for (j = 0; j < item->origin.path_len; ++j) { - memcpy(p, &item->origin.path[j], sizeof(uint32_t)); - p += 4; - } + push_keypath_item(cursor, max, + WALLY_PSBT_IN_BIP32_DERIVATION, + &keypaths->items[i]); } } /* Final scriptSig */ if (input->final_script_sig) { - type = WALLY_PSBT_IN_FINAL_SCRIPTSIG; - p += varbuff_to_bytes(&type, 1, p); - p += varbuff_to_bytes(input->final_script_sig, input->final_script_sig_len, p); + push_psbt_key(cursor, max, WALLY_PSBT_IN_FINAL_SCRIPTSIG, NULL, 0); + push_varbuff(cursor, max, + input->final_script_sig, input->final_script_sig_len); } /* Final scriptWitness */ if (input->final_witness) { - struct wally_tx_witness_stack *witness = input->final_witness; - size_t wit_len = varint_get_length(witness->num_items); - type = WALLY_PSBT_IN_FINAL_SCRIPTWITNESS; - p += varbuff_to_bytes(&type, 1, p); - for (i = 0; i < witness->num_items; ++i) { - const struct wally_tx_witness_item *stack; - stack = witness->items + i; - wit_len += varint_get_length(stack->witness_len); - wit_len += stack->witness_len; - } + size_t wit_len; + + push_psbt_key(cursor, max, WALLY_PSBT_IN_FINAL_SCRIPTWITNESS, NULL, 0); - p += varint_to_bytes(wit_len, p); - p += varint_to_bytes(witness->num_items, p); - for (i = 0; i < witness->num_items; ++i) { - const struct wally_tx_witness_item *stack; - stack = witness->items + i; - p += varbuff_to_bytes(stack->witness, stack->witness_len, p); + /* First pass simply calculates length */ + wit_len = 0; + push_witness_stack(NULL, &wit_len, input->final_witness); + + push_varint(cursor, max, wit_len); + push_witness_stack(cursor, max, input->final_witness); + } +#ifdef BUILD_ELEMENTS + /* Confidential Assets blinding data */ + if (input->has_value) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_VALUE, NULL, 0); + push_varint(cursor, max, sizeof(leint64_t)); + push_le64(cursor, max, input->value); + } + if (input->value_blinder) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_VALUE_BLINDER, NULL, 0); + push_varbuff(cursor, max, input->value_blinder, input->value_blinder_len); + } + if (input->asset) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_ASSET, NULL, 0); + push_varbuff(cursor, max, input->asset, input->asset_len); + } + if (input->asset_blinder) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_ASSET_BLINDER, NULL, 0); + push_varbuff(cursor, max, input->asset_blinder, input->asset_blinder_len); + } + /* Peg ins */ + if (input->peg_in_tx) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX, NULL, 0); + ret = push_length_and_tx(cursor, max, + input->peg_in_tx, + WALLY_TX_FLAG_USE_WITNESS, false); + if (ret != WALLY_OK) { + return ret; } } + if (input->txout_proof) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_TXOUT_PROOF, NULL, 0); + push_varbuff(cursor, max, input->txout_proof, input->txout_proof_len); + } + if (input->genesis_hash) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_GENESIS_HASH, NULL, 0); + push_varbuff(cursor, max, input->genesis_hash, input->genesis_hash_len); + } + if (input->claim_script) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_CLAIM_SCRIPT, NULL, 0); + push_varbuff(cursor, max, input->claim_script, input->claim_script_len); + } +#endif /* BUILD_ELEMENTS */ /* Unknowns */ if (input->unknowns) { for (i = 0; i < input->unknowns->num_items; ++i) { struct wally_unknowns_item *unknown = &input->unknowns->items[i]; - p += varint_to_bytes(unknown->key_len, p); - memcpy(p, unknown->key, unknown->key_len); - p += unknown->key_len; - p += varint_to_bytes(unknown->value_len, p); - memcpy(p, unknown->value, unknown->value_len); - p += unknown->value_len; + push_varbuff(cursor, max, unknown->key, unknown->key_len); + push_varbuff(cursor, max, unknown->value, unknown->value_len); } } /* Separator */ - *p = WALLY_PSBT_SEPARATOR; - p++; - - *bytes_written = p - bytes_out; - + push_u8(cursor, max, WALLY_PSBT_SEPARATOR); return WALLY_OK; } -static int psbt_output_to_bytes( - const struct wally_psbt_output *output, - unsigned char *bytes_out, - size_t *bytes_written) +static int push_psbt_output( + unsigned char **cursor, size_t *max, + const struct wally_psbt_output *output) { - unsigned char type, *p = bytes_out; size_t i; /* Redeem script */ if (output->redeem_script) { - type = WALLY_PSBT_OUT_REDEEM_SCRIPT; - p += varbuff_to_bytes(&type, 1, p); - p += varbuff_to_bytes(output->redeem_script, output->redeem_script_len, p); + push_psbt_key(cursor, max, WALLY_PSBT_OUT_REDEEM_SCRIPT, NULL, 0); + push_varbuff(cursor, max, + output->redeem_script, output->redeem_script_len); } /* Witness script */ if (output->witness_script) { - type = WALLY_PSBT_OUT_WITNESS_SCRIPT; - p += varbuff_to_bytes(&type, 1, p); - p += varbuff_to_bytes(output->witness_script, output->witness_script_len, p); + push_psbt_key(cursor, max, WALLY_PSBT_OUT_WITNESS_SCRIPT, NULL, 0); + push_varbuff(cursor, max, + output->witness_script, output->witness_script_len); } /* Keypaths */ if (output->keypaths) { struct wally_keypath_map *keypaths = output->keypaths; for (i = 0; i < keypaths->num_items; ++i) { - size_t origin_len, j; - struct wally_keypath_item *item = &keypaths->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - p += varint_to_bytes(34, p); - *p = WALLY_PSBT_OUT_BIP32_DERIVATION; - p++; - memcpy(p, item->pubkey, EC_PUBLIC_KEY_LEN); - p += EC_PUBLIC_KEY_LEN; - } else { - p += varint_to_bytes(66, p); - *p = WALLY_PSBT_OUT_BIP32_DERIVATION; - p++; - memcpy(p, item->pubkey, EC_PUBLIC_KEY_UNCOMPRESSED_LEN); - p += EC_PUBLIC_KEY_UNCOMPRESSED_LEN; - } - - origin_len = 4; /* Start with 4 bytes for fingerprint */ - origin_len += item->origin.path_len * sizeof(uint32_t); - p += varint_to_bytes(origin_len, p); - - memcpy(p, item->origin.fingerprint, 4); - p += 4; - for (j = 0; j < item->origin.path_len; ++j) { - memcpy(p, &item->origin.path[j], sizeof(uint32_t)); - p += 4; - } + push_keypath_item(cursor, max, + WALLY_PSBT_OUT_BIP32_DERIVATION, + &keypaths->items[i]); + } + } +#ifdef BUILD_ELEMENTS + if (output->value_commitment) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT, NULL, 0); + push_varbuff(cursor, max, output->value_commitment, output->value_commitment_len); + } + if (output->value_blinder) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_VALUE_BLINDER, NULL, 0); + push_varbuff(cursor, max, output->value_blinder, output->value_blinder_len); + } + if (output->asset_commitment) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_ASSET_COMMITMENT, NULL, 0); + push_varbuff(cursor, max, output->asset_commitment, output->asset_commitment_len); + } + if (output->asset_blinder) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_ASSET_BLINDER, NULL, 0); + push_varbuff(cursor, max, output->asset_blinder, output->asset_blinder_len); + } + if (output->range_proof) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_RANGE_PROOF, NULL, 0); + push_varbuff(cursor, max, output->range_proof, output->range_proof_len); + } + if (output->surjection_proof) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_SURJECTION_PROOF, NULL, 0); + push_varbuff(cursor, max, output->surjection_proof, output->surjection_proof_len); + } + if (output->has_blinding_pubkey) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_BLINDING_PUBKEY, NULL, 0); + if (pubkey_is_compressed(output->blinding_pubkey)) { + push_varbuff(cursor, max, output->blinding_pubkey, EC_PUBLIC_KEY_LEN); + } + else { + push_varbuff(cursor, max, output->blinding_pubkey, EC_PUBLIC_KEY_UNCOMPRESSED_LEN); } } + if (output->nonce_commitment) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_NONCE_COMMITMENT, NULL, 0); + push_varbuff(cursor, max, output->nonce_commitment, output->nonce_commitment_len); + } +#endif /* BUILD_ELEMENTS */ /* Unknowns */ if (output->unknowns) { for (i = 0; i < output->unknowns->num_items; ++i) { struct wally_unknowns_item *unknown = &output->unknowns->items[i]; - p += varint_to_bytes(unknown->key_len, p); - memcpy(p, unknown->key, unknown->key_len); - p += unknown->key_len; - p += varint_to_bytes(unknown->value_len, p); - memcpy(p, unknown->value, unknown->value_len); - p += unknown->value_len; + push_varbuff(cursor, max, unknown->key, unknown->key_len); + push_varbuff(cursor, max, unknown->value, unknown->value_len); } } /* Separator */ - *p = WALLY_PSBT_SEPARATOR; - p++; - - *bytes_written = p - bytes_out; - + push_u8(cursor, max, WALLY_PSBT_SEPARATOR); return WALLY_OK; } @@ -2048,77 +2966,65 @@ int wally_psbt_to_bytes( unsigned char *bytes_out, size_t len, size_t *bytes_written) { - unsigned char type, *p = bytes_out, *end = bytes_out + len; - size_t calc_len, tx_len, i; + unsigned char *cursor = bytes_out; + size_t max = len, i; int ret; - if (bytes_written) { - *bytes_written = 0; - } - - ret = wally_psbt_get_length(psbt, &calc_len); - if (ret != WALLY_OK) { - return ret; - } - if (calc_len > len) { - return WALLY_EINVAL; /* Buffer is not big enough */ - } + *bytes_written = 0; - /* Magic */ - memcpy(p, WALLY_PSBT_MAGIC, 5); - p += 5; + push_bytes(&cursor, &max, psbt->magic, sizeof(psbt->magic)); /* Global tx */ - type = WALLY_PSBT_GLOBAL_UNSIGNED_TX; - p += varbuff_to_bytes(&type, 1, p); - ret = wally_tx_get_length(psbt->tx, 0, &tx_len); - if (ret != WALLY_OK) { - return ret; - } - p += varint_to_bytes(tx_len, p); - ret = wally_tx_to_bytes(psbt->tx, 0, p, end - p, &tx_len); - if (ret != WALLY_OK) { - return ret; + push_psbt_key(&cursor, &max, WALLY_PSBT_GLOBAL_UNSIGNED_TX, NULL, 0); + push_length_and_tx(&cursor, &max, psbt->tx, 0, true); + + /* version */ + if (psbt->version > 0) { + push_psbt_key(&cursor, &max, WALLY_PSBT_GLOBAL_VERSION, NULL, 0); + push_varint(&cursor, &max, sizeof(uint32_t)); + push_le32(&cursor, &max, psbt->version); } - p += tx_len; /* Unknowns */ if (psbt->unknowns) { for (i = 0; i < psbt->unknowns->num_items; ++i) { struct wally_unknowns_item *unknown = &psbt->unknowns->items[i]; - p += varint_to_bytes(unknown->key_len, p); - memcpy(p, unknown->key, unknown->key_len); - p += unknown->key_len; - p += varint_to_bytes(unknown->value_len, p); - memcpy(p, unknown->value, unknown->value_len); - p += unknown->value_len; + push_varbuff(&cursor, &max, unknown->key, unknown->key_len); + push_varbuff(&cursor, &max, unknown->value, unknown->value_len); } } /* Separator */ - *p = WALLY_PSBT_SEPARATOR; - p++; + push_u8(&cursor, &max, WALLY_PSBT_SEPARATOR); - /* Get lengths of each input and output */ + /* Push each input and output */ + size_t is_elements; + if ((ret = wally_psbt_is_elements(psbt, &is_elements)) != WALLY_OK) { + return ret; + } for (i = 0; i < psbt->num_inputs; ++i) { struct wally_psbt_input *input = &psbt->inputs[i]; - size_t input_len; - ret = psbt_input_to_bytes(input, p, end - p, &input_len); + ret = push_psbt_input(&cursor, &max, is_elements == 1, input); if (ret != WALLY_OK) { return ret; } - p += input_len; } for (i = 0; i < psbt->num_outputs; ++i) { struct wally_psbt_output *output = &psbt->outputs[i]; - size_t output_len; - ret = psbt_output_to_bytes(output, p, &output_len); + ret = push_psbt_output(&cursor, &max, output); if (ret != WALLY_OK) { return ret; } - p += output_len; } - *bytes_written = p - bytes_out; + + if (cursor == NULL) { + /* Once cursor was NULL, max accumulates hm bytes we needed */ + *bytes_written = len + max; + return WALLY_EINVAL; + } else { + *bytes_written = len - max; + } + return WALLY_OK; } @@ -2523,11 +3429,17 @@ int wally_sign_psbt( unsigned char pubkey[EC_PUBLIC_KEY_LEN], uncomp_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN], sig[EC_SIGNATURE_LEN], der_sig[EC_SIGNATURE_DER_MAX_LEN + 1]; size_t i, j, der_sig_len; int ret; + bool is_elements; if (!psbt || !psbt->tx || !key || key_len != EC_PRIVATE_KEY_LEN) { return WALLY_EINVAL; } + if ((ret = wally_psbt_is_elements(psbt, &i)) != WALLY_OK) { + return ret; + } + is_elements = i == 1; + /* Get the pubkey */ if ((ret = wally_ec_public_key_from_private_key(key, key_len, pubkey, EC_PUBLIC_KEY_LEN)) != WALLY_OK) { return ret; @@ -2668,7 +3580,15 @@ int wally_sign_psbt( continue; } - if ((ret = wally_tx_get_btc_signature_hash(psbt->tx, i, scriptcode, scriptcode_len, input->witness_utxo->satoshi, sighash_type, WALLY_TX_FLAG_USE_WITNESS, sighash, SHA256_LEN)) != WALLY_OK) { + if (is_elements) { +#ifdef BUILD_ELEMENTS + if ((ret = wally_tx_get_elements_signature_hash(psbt->tx, i, scriptcode, scriptcode_len, input->witness_utxo->value, input->witness_utxo->value_len, sighash_type, WALLY_TX_FLAG_USE_WITNESS, sighash, SHA256_LEN)) != WALLY_OK) { + return ret; + } +#else + return WALLY_ERROR; +#endif /* BUILD_ELEMENTS */ + } else if ((ret = wally_tx_get_btc_signature_hash(psbt->tx, i, scriptcode, scriptcode_len, input->witness_utxo->satoshi, sighash_type, WALLY_TX_FLAG_USE_WITNESS, sighash, SHA256_LEN)) != WALLY_OK) { return ret; } } @@ -2986,3 +3906,19 @@ int wally_extract_psbt( } return ret; } + +int wally_psbt_is_elements( + const struct wally_psbt *psbt, + size_t *written) +{ + if (!psbt || !written) + return WALLY_EINVAL; + + *written = 0; +#ifdef BUILD_ELEMENTS + if (memcmp(psbt->magic, WALLY_ELEMENTS_PSBT_MAGIC, 5) == 0) { + *written = 1; + } +#endif /* BUILD_ELEMENTS */ + return WALLY_OK; +} diff --git a/src/pullpush.c b/src/pullpush.c new file mode 100644 index 000000000..03eb9ad34 --- /dev/null +++ b/src/pullpush.c @@ -0,0 +1,198 @@ +#include "internal.h" +#include "script_int.h" + +#include +#include +#include +#include "pullpush.h" + +unsigned char *push_bytes(unsigned char **cursor, size_t *max, + const void *src, size_t len) +{ + if (cursor == NULL || *cursor == NULL) { + *max += len; + return NULL; + } + if (len > *max) { + if (src) + memcpy(*cursor, src, *max); + /* From now on, max records the room we *needed* */ + *max = len - *max; + *cursor = NULL; + return NULL; + } + if (src) + memcpy(*cursor, src, len); + + *cursor += len; + *max -= len; + + return *cursor - len; +} + +void pull_bytes(void *dst, size_t len, + const unsigned char **cursor, size_t *max) +{ + if (len > *max) { + memcpy(dst, *cursor, *max); + memset((char *)dst + *max, 0, len - *max); + pull_failed(cursor, max); + return; + } + memcpy(dst, *cursor, len); + *cursor += len; + *max -= len; +} + +const unsigned char *pull_skip(const unsigned char **cursor, size_t *max, + size_t len) +{ + const unsigned char *p; + + if (*cursor == NULL) { + return NULL; + } + + if (len > *max) { + pull_failed(cursor, max); + return NULL; + } + + p = *cursor; + *cursor += len; + *max -= len; + return p; +} + +void pull_failed(const unsigned char **cursor, size_t *max) +{ + *cursor = NULL; + *max = 0; +} + +void push_le64(unsigned char **cursor, size_t *max, uint64_t v) +{ + leint64_t lev = cpu_to_le64(v); + push_bytes(cursor, max, &lev, sizeof(lev)); +} + +uint64_t pull_le64(const unsigned char **cursor, size_t *max) +{ + leint64_t lev; + pull_bytes(&lev, sizeof(lev), cursor, max); + return le64_to_cpu(lev); +} + +void push_le32(unsigned char **cursor, size_t *max, uint32_t v) +{ + leint32_t lev = cpu_to_le32(v); + push_bytes(cursor, max, &lev, sizeof(lev)); +} + +uint32_t pull_le32(const unsigned char **cursor, size_t *max) +{ + leint32_t lev; + pull_bytes(&lev, sizeof(lev), cursor, max); + return le32_to_cpu(lev); +} + +void push_le16(unsigned char **cursor, size_t *max, uint16_t v) +{ + leint16_t lev = cpu_to_le16(v); + push_bytes(cursor, max, &lev, sizeof(lev)); +} + +uint16_t pull_le16(const unsigned char **cursor, size_t *max) +{ + leint16_t lev; + pull_bytes(&lev, sizeof(lev), cursor, max); + return le16_to_cpu(lev); +} + +void push_u8(unsigned char **cursor, size_t *max, uint8_t v) +{ + push_bytes(cursor, max, &v, sizeof(uint8_t)); +} + +uint8_t pull_u8(const unsigned char **cursor, size_t *max) +{ + uint8_t v; + pull_bytes(&v, sizeof(v), cursor, max); + return v; +} + +uint8_t peek_u8(const unsigned char **cursor, size_t *max) +{ + uint8_t v = pull_u8(cursor, max); + if (*cursor) { + *cursor -= sizeof(v); + *max += sizeof(v); + } + return v; +} + +void push_varint(unsigned char **cursor, size_t *max, uint64_t v) +{ + unsigned char buf[sizeof(uint8_t) + sizeof(uint64_t)]; + size_t len = varint_to_bytes(v, buf); + + push_bytes(cursor, max, buf, len); +} + +uint64_t pull_varint(const unsigned char **cursor, size_t *max) +{ + unsigned char buf[sizeof(uint8_t) + sizeof(uint64_t)]; + uint64_t v; + + /* FIXME: Would be more efficient to opencode varint here! */ + pull_bytes(buf, 1, cursor, max); + pull_bytes(buf + 1, varint_length_from_bytes(buf) - 1, cursor, max); + varint_from_bytes(buf, &v); + + return v; +} + +void push_varbuff(unsigned char **cursor, size_t *max, + const unsigned char *bytes, size_t bytes_len) +{ + push_varint(cursor, max, bytes_len); + push_bytes(cursor, max, bytes, bytes_len); +} + +size_t pull_varlength(const unsigned char **cursor, size_t *max) +{ + uint64_t len = pull_varint(cursor, max); + + if (len > *max) { + /* Impossible length. */ + pull_failed(cursor, max); + return 0; + } + return len; +} + +void pull_subfield_start(const unsigned char *const *cursor, const size_t *max, + size_t subfield_len, + const unsigned char **subcursor, size_t *submax) +{ + if (subfield_len > *max) { + pull_failed(subcursor, submax); + } else { + *subcursor = *cursor; + *submax = subfield_len; + } +} + +void pull_subfield_end(const unsigned char **cursor, size_t *max, + const unsigned char *subcursor, size_t submax) +{ + if (subcursor == NULL) { + pull_failed(cursor, max); + } else if (*cursor != NULL) { + const unsigned char *subend = subcursor + submax; + assert(subcursor >= *cursor); + assert(subend <= *cursor + *max); + *max -= (subend - *cursor); + *cursor = subend; + } +} diff --git a/src/pullpush.h b/src/pullpush.h new file mode 100644 index 000000000..3922d487b --- /dev/null +++ b/src/pullpush.h @@ -0,0 +1,92 @@ +#ifndef LIBWALLY_CORE_PULLPUSH_H +#define LIBWALLY_CORE_PULLPUSH_H 1 +/** + * Safely copy @len bytes from @src into @cursor. + * + * If @cursor is NULL or *@cursor is NULL, then nothing will be + * written to it (obv), but *@max will be increased @len. + * + * Otherwise, if @len > *@max, *@max bytes will be copied from @src to + * *@cursor if @src is not NULL, then *@cursor will be set to NULL and + * @max will be set to the remainder. + * + * Otherwise, @len bytes will be copied from @src to *@cursor if @src + * is not NULL, *@max decreased by @len and @cursor increased by @len. + * + * This means you can call it repeatedly, and if it fails, *@cursor will be + * NULL, and *@max will indicate how many additional bytes you need. + * + * On success, this returns *@cursor before it was updated, otherwise NULL. + */ +unsigned char *push_bytes(unsigned char **cursor, size_t *max, + const void *src, size_t len); + +/** + * Safely copy @len bytes from @cursor into @dst. + * + * If @len > *@max, *@max bytes will be copied from *@cursor, the + * remainder of @dst will be zeroed, then *@cursor will be set to NULL + * and @max will be set to 0. + * Otherwise, @len bytes will be copied from @cursor, *@max decreased + * by @len and @cursor increased by @len. + */ +void pull_bytes(void *dst, size_t len, + const unsigned char **cursor, size_t *max); + + +/** + * Return a pointer to (and skip over) some bytes. + * + * Returns NULL (and maybe calls pull_failed()) if there are not enough left. + */ +const unsigned char *pull_skip(const unsigned char **cursor, size_t *max, + size_t len); + + +/** + * Convenience function to indicate a pull failed (eg. type decode failed) + * + * Sets *cursor to NULL, *max to 0. + */ +void pull_failed(const unsigned char **cursor, size_t *max); + +/** + * Convenience functions. + */ +void push_le64(unsigned char **cursor, size_t *max, uint64_t v); +uint64_t pull_le64(const unsigned char **cursor, size_t *max); +void push_le32(unsigned char **cursor, size_t *max, uint32_t v); +uint32_t pull_le32(const unsigned char **cursor, size_t *max); +void push_le16(unsigned char **cursor, size_t *max, uint16_t v); +uint16_t pull_le16(const unsigned char **cursor, size_t *max); +void push_u8(unsigned char **cursor, size_t *max, uint8_t v); +uint8_t pull_u8(const unsigned char **cursor, size_t *max); +uint8_t peek_u8(const unsigned char **cursor, size_t *max); + +void push_varint(unsigned char **cursor, size_t *max, uint64_t v); +uint64_t pull_varint(const unsigned char **cursor, size_t *max); + +void push_varbuff(unsigned char **cursor, size_t *max, + const unsigned char *bytes, size_t bytes_len); + +/* Calls pull_failed() (and returns 0) if length would exceed remaining *max */ +size_t pull_varlength(const unsigned char **cursor, size_t *max); + +/** + * Functions to parse a subfield within an area. + * + * pull_subfield_start: initializes subcursor/submax to subfield_len at + * cursor. These can then be used with pull_ methods like normal. + * + * end_subfield_pull: updates *cursor and *max to point after the subfield has + * been parsed. If *subcursor is NULL, it's equivalent to pull_failed. + */ +void pull_subfield_start(const unsigned char *const *cursor, const size_t *max, + size_t subfield_len, + const unsigned char **subcursor, size_t *submax); + +void pull_subfield_end(const unsigned char **cursor, size_t *max, + const unsigned char *subcursor, + const size_t submax); + +#endif /* LIBWALLY_CORE_PULLPUSH_H */ diff --git a/src/test/data/psbt.json b/src/test/data/psbt.json index d17719b96..63b795e1c 100644 --- a/src/test/data/psbt.json +++ b/src/test/data/psbt.json @@ -17,7 +17,15 @@ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", - "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" + "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", + "cHNidP8B+wQBAAAAAQB1AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAAAA/v///wLT3/UFAAAAABl2qRTQxZkDxbrChodg6Q/VIaRmWqdlIIisAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4ezLhMAAAEA/aUBAQAAAAABAomjxx6rTSDgNxu7pMxpj6KVyUY6+i45f4UzzLYvlWflAQAAABcWABS+GNFSqbASA52vPafeT1M0nuy5hf////+G+KpDpx3/FEiJOlMKcjfva0YIu7LdLQFx5jrsakiQtAEAAAAXFgAU/j6e8adF6XTZAsQ1WUOryzS9U1P/////AgDC6wsAAAAAGXapFIXP8Ql/2eAIuzSvcJxiGXs4l4pIiKxy/vhOLAAAABepFDOXJboh79Yqx1OpvNBn1semo50FhwJHMEQCICcSviLgJw85T1aDEdx8qaaJcLgCX907JAIp8H+KXzokAiABizjX3NMU5zTJJ2vW+0D2czJbxLqhRMgA0vLwLbJ2XAEhA9LhVnSUG61KmWNyy4fhhW02UmBtmFYv45xenn5BPyEFAkgwRQIhANErhS2F3Nlh0vX0q2YGVN9u7cx5TAwzzlzDCf+1/OWNAiBnM4qODhclwZf7GoivWfUeROQlWyAWfIaEAxwF0fJZKgEhAiO3K+7wll0Qvgd47+zWH8rG95pOoWk5M4BzRGT4TyqzAAAAAAAAAA==", + "cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQAAAQABagA=", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJ//////////8AAQEJAADK/gAAAAAAAA==", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQMErd7f7gEDBAEAAAAA", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQQAAQQBagA=", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQUAAQUBUQA=", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA=", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA=" ], "valid" : [ { diff --git a/src/test/data/pset.json b/src/test/data/pset.json new file mode 100644 index 000000000..4b49da887 --- /dev/null +++ b/src/test/data/pset.json @@ -0,0 +1,6 @@ +{ + "valid": [{ + "pset": "cHNldP8BAOYCAAAAAAEJoA5k8iAABF66mJyk85LBsCUTVVfErOwxmexTjCBEyQAAAAAA/f///wMBXOe5Y9N/jy1Ryvu6koqqniILi7xmBXFJnANiijhRuM4BAAAAAO5rFFgAF6kUpruHAKCJvLhrCf5VkrhUvQ4+bDKHAVznuWPTf48tUcr7upKKqp4iC4u8ZgVxSZwDYoo4UbjOAQAAAAA7msoAABepFII2rTwCO4NdVWyoBFVzHVM/c1IBhwFc57lj03+PLVHK+7qSiqqeIguLvGYFcUmcA2KKOFG4zgEAAAAAAAATqAAAAAAAAAABAUMBXOe5Y9N/jy1Ryvu6koqqniILi7xmBXFJnANiijhRuM4BAAAAASoF8gAAF6kUIUgXxch89mjFY+aFBdmAk9d8GcmHAQQWABTs8/AAycufhZ2FR4uYndwqd1C7ogv8CGVsZW1lbnRzAAgA8gUqAQAAAAv8CGVsZW1lbnRzAiBc57lj03+PLVHK+7qSiqqeIguLvGYFcUmcA2KKOFG4zgABABYAFMFIn/H6YCLLR2ukTXG9evUf5T5VC/wIZWxlbWVudHMGIQMYlbtO8ZBg4wdFP1vS+d59msC7iEjmr/VATyVestonAwABABYAFJpBlksv6mEJcXOPlyskNv1O3yn+C/wIZWxlbWVudHMGIQLkIFPXDMnWVGXBPhDwpi5zMF/WmosNkQLltvC445AA2QAA", + "len": 546 + }] +} diff --git a/src/test/test_pset.py b/src/test/test_pset.py new file mode 100644 index 000000000..4af6e851f --- /dev/null +++ b/src/test/test_pset.py @@ -0,0 +1,31 @@ +import json +import os +import unittest +from util import * + +class PSETTests(unittest.TestCase): + + def test_serialization(self): + """Testing serialization and deserialization""" + self.maxDiff = None + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/pset.json')) as f: + d = json.load(f) + valids = d['valid'] + + for valid in valids: + psbt = pointer(wally_psbt()) + self.assertEqual(WALLY_OK, wally_psbt_from_base64(valid['pset'].encode('utf-8'), psbt)) + ret, reser = wally_psbt_to_base64(psbt) + self.assertEqual(WALLY_OK, ret) + self.assertEqual(valid['pset'], reser) + ret, length = wally_psbt_get_length(psbt) + self.assertEqual(WALLY_OK, ret) + self.assertEqual(length, valid['len']) + + +if __name__ == '__main__': + _, val = wally_is_elements_build() + if val != 0: + unittest.main() + else: + self.fail("Attempting to run Elements test without Elements enabled") diff --git a/src/test/util.py b/src/test/util.py index 0b197e77f..27463c3b3 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -160,7 +160,22 @@ class wally_psbt_input(Structure): ('keypaths', POINTER(keypath_map)), ('partial_sigs', POINTER(partial_sigs_map)), ('unknowns', POINTER(unknowns_map)), - ('sighash_type', c_ulong)] + ('sighash_type', c_ulong), + ('value', c_ulonglong), + ('has_value', c_ulong), + ('value_blinder', c_void_p), + ('value_blinder_len', c_ulong), + ('asset', c_void_p), + ('asset_len', c_ulong), + ('asset_blinder', c_void_p), + ('asset_blinder_len', c_ulong), + ('peg_in_tx', POINTER(wally_tx)), + ('txout_proof', c_void_p), + ('txout_proof_len', c_ulong), + ('genesis_hash', c_void_p), + ('genesis_hash_len', c_ulong), + ('claim_script', c_void_p), + ('claim_script_len', c_ulong)] class wally_psbt_output(Structure): _fields_ = [('redeem_script', c_void_p), @@ -168,17 +183,35 @@ class wally_psbt_output(Structure): ('witness_script', c_void_p), ('witness_script_len', c_ulong), ('keypaths', POINTER(keypath_map)), - ('unknowns', POINTER(unknowns_map))] + ('unknowns', POINTER(unknowns_map)), + ('blinding_pubkey', c_ubyte * 65), + ('has_blinding_pubkey', c_ulong), + ('value_commitment', c_void_p), + ('value_commitment_len', c_ulong), + ('value_blinder', c_void_p), + ('value_blinder_len', c_ulong), + ('asset_commitment', c_void_p), + ('asset_commitment_len', c_ulong), + ('asset_blinder', c_void_p), + ('asset_blinder_len', c_ulong), + ('nonce_commitment', c_void_p), + ('nonce_commitment_len', c_ulong), + ('range_proof', c_void_p), + ('range_proof_len', c_ulong), + ('surjection_proof', c_void_p), + ('surjection_proof_len', c_ulong)] class wally_psbt(Structure): - _fields_ = [('tx', POINTER(wally_tx)), + _fields_ = [('magic', c_ubyte * 5), + ('tx', POINTER(wally_tx)), ('inputs', POINTER(wally_psbt_input)), ('num_inputs', c_ulong), ('inputs_allocation_len', c_ulong), ('outputs', POINTER(wally_psbt_output)), ('num_outputs', c_ulong), ('outputs_allocation_len', c_ulong), - ('unknowns', POINTER(unknowns_map))] + ('unknowns', POINTER(unknowns_map)), + ('version', c_ulong)] for f in ( ('wally_init', c_int, [c_uint]), diff --git a/src/transaction.c b/src/transaction.c index e87ec6d3c..7f47917c5 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -775,8 +775,8 @@ int wally_tx_input_free(struct wally_tx_input *input) return tx_input_free(input, true); } -static bool clone_output_to(struct wally_tx_output *dst, - const struct wally_tx_output *src) +int wally_clone_output_to(struct wally_tx_output *dst, + const struct wally_tx_output *src) { unsigned char *new_script = NULL; #ifdef BUILD_ELEMENTS @@ -784,7 +784,7 @@ static bool clone_output_to(struct wally_tx_output *dst, *new_surjectionproof = NULL, *new_rangeproof = NULL; #endif if (!dst || !src) - return false; + return WALLY_EINVAL; #ifdef BUILD_ELEMENTS if (!clone_bytes(&new_asset, src->asset, src->asset_len) || @@ -804,7 +804,7 @@ static bool clone_output_to(struct wally_tx_output *dst, clear_and_free(new_surjectionproof, src->surjectionproof_len); clear_and_free(new_rangeproof, src->rangeproof_len); #endif - return false; + return WALLY_ENOMEM; } memcpy(dst, src, sizeof(*src)); @@ -816,7 +816,7 @@ static bool clone_output_to(struct wally_tx_output *dst, dst->surjectionproof = new_surjectionproof; dst->rangeproof = new_rangeproof; #endif - return true; + return WALLY_OK; } static int tx_elements_output_proof_init( @@ -1358,6 +1358,7 @@ int wally_tx_remove_input(struct wally_tx *tx, size_t index) int wally_tx_add_output(struct wally_tx *tx, const struct wally_tx_output *output) { uint64_t total; + int ret; const bool is_elements = output->features & WALLY_TX_IS_ELEMENTS; if (!is_elements) { if (!is_valid_tx(tx) || !is_valid_tx_output(output) || @@ -1379,8 +1380,8 @@ int wally_tx_add_output(struct wally_tx *tx, const struct wally_tx_output *outpu tx->outputs = p; tx->outputs_allocation_len += 1; } - if (!clone_output_to(tx->outputs + tx->num_outputs, output)) - return WALLY_ENOMEM; + if ((ret = wally_clone_output_to(tx->outputs + tx->num_outputs, output)) != WALLY_OK) + return ret; tx->num_outputs += 1; return WALLY_OK; @@ -1972,7 +1973,8 @@ static int tx_to_bytes(const struct wally_tx *tx, uint32_t flags, unsigned char *bytes_out, size_t len, size_t *written, - bool is_elements) + bool is_elements, + bool partial_ok) { size_t n, i, j, witness_count; const bool anyonecanpay = opts && opts->sighash & WALLY_SIGHASH_ANYONECANPAY; @@ -1983,11 +1985,14 @@ static int tx_to_bytes(const struct wally_tx *tx, if (written) *written = 0; - if (!is_valid_tx(tx) || !tx->num_inputs || !tx->num_outputs || + if (!is_valid_tx(tx) || (flags & ~WALLY_TX_FLAG_USE_WITNESS) || !bytes_out || !written || tx_get_length(tx, opts, flags, &n, is_elements) != WALLY_OK) return WALLY_EINVAL; + if (!partial_ok && (!tx->num_inputs || !tx->num_outputs)) + return WALLY_EINVAL; + if (opts && (flags & WALLY_TX_FLAG_USE_WITNESS)) return WALLY_ERROR; /* Segwit tx hashing is handled elsewhere */ @@ -2138,9 +2143,10 @@ static int tx_to_bytes(const struct wally_tx *tx, return WALLY_OK; } -int wally_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, - unsigned char *bytes_out, size_t len, - size_t *written) +int wally_partial_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, + unsigned char *bytes_out, size_t len, + bool partial_ok, + size_t *written) { size_t is_elements = 0; @@ -2148,7 +2154,16 @@ int wally_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, if (wally_tx_is_elements(tx, &is_elements) != WALLY_OK) return WALLY_EINVAL; #endif - return tx_to_bytes(tx, NULL, flags, bytes_out, len, written, is_elements); + return tx_to_bytes(tx, NULL, flags, bytes_out, len, written, is_elements, + partial_ok); +} + + +int wally_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, + unsigned char *bytes_out, size_t len, + size_t *written) +{ + return wally_partial_tx_to_bytes(tx, flags, bytes_out, len, false, written); } static int tx_to_hex(const struct wally_tx *tx, uint32_t flags, @@ -2161,12 +2176,12 @@ static int tx_to_hex(const struct wally_tx *tx, uint32_t flags, if (!output) return WALLY_EINVAL; - ret = tx_to_bytes(tx, NULL, flags, buff_p, sizeof(buff), &n, is_elements); + ret = tx_to_bytes(tx, NULL, flags, buff_p, sizeof(buff), &n, is_elements, false); if (ret == WALLY_OK) { if (n > sizeof(buff)) { if ((buff_p = wally_malloc(n)) == NULL) return WALLY_ENOMEM; - ret = tx_to_bytes(tx, NULL, flags, buff_p, n, &written, is_elements); + ret = tx_to_bytes(tx, NULL, flags, buff_p, n, &written, is_elements, false); if (n != written) ret = WALLY_ERROR; /* Length calculated incorrectly */ } @@ -2669,7 +2684,7 @@ static int tx_get_signature_hash(const struct wally_tx *tx, goto fail; } - if ((ret = tx_to_bytes(tx, &opts, 0, buff_p, n, &n2, is_elements != 0)) != WALLY_OK) + if ((ret = tx_to_bytes(tx, &opts, 0, buff_p, n, &n2, is_elements != 0, false)) != WALLY_OK) goto fail; if (n != n2) diff --git a/src/transaction_shared.h b/src/transaction_shared.h index 109b7c0f1..77a2eca1e 100644 --- a/src/transaction_shared.h +++ b/src/transaction_shared.h @@ -20,6 +20,10 @@ int analyze_tx(const unsigned char *bytes, size_t bytes_len, struct wally_tx_witness_stack *clone_witness( const struct wally_tx_witness_stack *stack); int clone_tx(struct wally_tx *tx, struct wally_tx **output); +int wally_partial_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, + unsigned char *bytes_out, size_t len, + bool partial_ok, + size_t *written); #ifdef __cplusplus } diff --git a/tools/travis_build.sh b/tools/travis_build.sh index 638c9e4dc..b218b3f37 100755 --- a/tools/travis_build.sh +++ b/tools/travis_build.sh @@ -1,21 +1,6 @@ #! /usr/bin/env bash -function show_err() -{ - if [ -f $1 ]; then - cat $1 - fi -} - -function show_test_err() -{ - tests="test_bech32 test_clear test_tx test_elements_tx test_blech32" - for i in $tests; do - show_err src/$i.log - done -} - -trap "show_test_err" ERR +trap "cat src/test_*.log" ERR ENABLE_SWIG_PYTHON="--enable-swig-python" ENABLE_SWIG_JAVA="--enable-swig-java"