diff --git a/.gitignore b/.gitignore index e5f983e03..f99c9bc9d 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 diff --git a/include/wally_psbt.h b/include/wally_psbt.h index 1743c72f3..825b6d49e 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -509,7 +509,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, diff --git a/src/Makefile.am b/src/Makefile.am index f69b4cefe..ec91673b2 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 diff --git a/src/ctest/psbts.h b/src/ctest/psbts.h new file mode 100644 index 000000000..51864c0eb --- /dev/null +++ b/src/ctest/psbts.h @@ -0,0 +1,299 @@ +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 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 bc9448758..51f95fde2 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -10,14 +10,16 @@ #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}; -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 +290,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)); @@ -831,6 +833,126 @@ 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; +} + struct psbt_input_counts { size_t num_unknowns; size_t num_keypaths; @@ -859,24 +981,14 @@ 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, 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 +997,32 @@ 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; + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { + const unsigned char *key; - if (key_len == 0) { - break; - } - - /* 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; + 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; } + ret = analyze_tx(val, val_max, 0, &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; @@ -928,40 +1033,27 @@ static int count_psbt_parts( /* 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: @@ -980,39 +1072,26 @@ static int count_psbt_parts( 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; @@ -1024,12 +1103,14 @@ static int count_psbt_parts( 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 +1123,15 @@ 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, 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 +1151,202 @@ 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: { 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); + + ret = wally_tx_from_bytes(val, val_max, 0, + &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); + 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) { 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; + } break; } case WALLY_PSBT_IN_WITNESS_SCRIPT: { if (result->witness_script_len != 0) { 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; + } 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) { 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; + } 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; - - 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; } -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 +1356,62 @@ 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 key value */ + pre_key = *cursor; + while ((key_len = pull_varlength(cursor, max)) != 0) { + const unsigned char *key; - /* 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_OUT_REDEEM_SCRIPT: { if (result->redeem_script_len != 0) { 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; + } break; } case WALLY_PSBT_OUT_WITNESS_SCRIPT: { if (result->witness_script_len != 0) { 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; + } 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 */ - } - /* 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; } /* 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,27 +1419,23 @@ 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; 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 ) { ret = WALLY_EINVAL; /* Invalid Magic */ goto fail; } - p += 5; /* Get a count of the psbt parts */ if (count_psbt_parts(bytes, bytes_len, &counts) != WALLY_OK) { @@ -1465,47 +1451,36 @@ 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; - } + pre_key = bytes; + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 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(&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; 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); + ret = wally_tx_from_bytes(val, val_max, 0, &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; } @@ -1514,23 +1489,19 @@ int wally_psbt_from_bytes( } /* 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 +1512,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], + &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 +1529,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 +1554,243 @@ 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); + ret = wally_psbt_to_bytes(psbt, NULL, 0, len); + if (ret == WALLY_EINVAL && *len != 0) { + return WALLY_OK; } - /* 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); - } - /* 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; + return ret; +} - *len = out; - return WALLY_OK; +/* 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) +{ + push_varint(cursor, max, varint_get_length(type) + extra_len); + push_varint(cursor, max, type); + push_bytes(cursor, max, extra, extra_len); } -static int psbt_output_get_length( - const struct wally_psbt_output *output, - size_t *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]) { - size_t out, i; - if (!len) { - return WALLY_EINVAL; + 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); } +} - *len = 0; - out = 0; +static int push_length_and_tx( + unsigned char **cursor, size_t *max, + const struct wally_tx *tx, uint32_t flags) +{ + int ret; + size_t txlen; + unsigned char *p; - /* 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); + ret = wally_tx_get_length(tx, flags, &txlen); + if (ret != WALLY_OK) { + return ret; } - /* 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 */ - } - 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); - } - } + push_varint(cursor, max, txlen); - /* Separator */ - out += 1; + /* FIXME: convert wally_tx to use push */ + p = push_bytes(cursor, max, NULL, txlen); + if (!p) { + /* We catch this in caller. */ + return WALLY_OK; + } - *len = out; - return WALLY_OK; + return wally_tx_to_bytes(tx, flags, p, txlen, &txlen); } -int wally_psbt_get_length( - const struct wally_psbt *psbt, - size_t *len) +static void push_witness_stack( + unsigned char **cursor, size_t *max, + const struct wally_tx_witness_stack *witness) { - int ret; - size_t out, tx_len, i; - if (!len) { - return WALLY_EINVAL; - } - - *len = 0; - out = 5; /* Start with 5 byte magic */ + size_t i; - /* Global tx */ - out += 2; - ret = wally_tx_get_length(psbt->tx, 0, &tx_len); - if (ret != WALLY_OK) { - return ret; + 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); } - 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); - } - } +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; - /* Separator */ - out += 1; + push_psbt_key_with_pubkey(cursor, max, type, item->pubkey); - /* 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; - } + origin_len = 4; /* Start with 4 bytes for fingerprint */ + origin_len += item->origin.path_len * sizeof(uint32_t); + push_varint(cursor, max, origin_len); - *len = out; - return WALLY_OK; + 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, + 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); if (ret != WALLY_OK) { return ret; } - p += tx_len; } + /* Witness utxo */ 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); - /* 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_psbt_key(cursor, max, WALLY_PSBT_IN_WITNESS_UTXO, NULL, 0); + 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; - 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); - } + push_psbt_key(cursor, max, WALLY_PSBT_IN_FINAL_SCRIPTWITNESS, NULL, 0); + + /* 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); } /* 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]); } } /* 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 +1799,54 @@ 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, WALLY_PSBT_MAGIC, sizeof(WALLY_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; - } - p += tx_len; + push_psbt_key(&cursor, &max, WALLY_PSBT_GLOBAL_UNSIGNED_TX, NULL, 0); + push_length_and_tx(&cursor, &max, psbt->tx, 0); /* 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 */ 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, 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; } diff --git a/src/pullpush.c b/src/pullpush.c new file mode 100644 index 000000000..0a1496c53 --- /dev/null +++ b/src/pullpush.c @@ -0,0 +1,188 @@ +#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; +} + +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..77d911f25 --- /dev/null +++ b/src/pullpush.h @@ -0,0 +1,91 @@ +#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); + +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/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"