From 60663a38353742258a6b554be0b780c8626b75c6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:39:04 +0930 Subject: [PATCH 01/37] travis: don't explicitly list what tests in tools/travis_build.sh AFAICT, the below does the same thing, and avoids glslang having to tell me to add my test there. Signed-off-by: Rusty Russell --- tools/travis_build.sh | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) 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" From 78a58b4e65d1eca0b964fb2f3050736a39cd7a89 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:41:09 +0930 Subject: [PATCH 02/37] psbt: add a simple test case, by copying some of BIP 0174. Signed-off-by: Rusty Russell --- .gitignore | 1 + src/Makefile.am | 5 + src/ctest/psbts.h | 299 ++++++++++++++++++++++++++++++++++++++++++ src/ctest/test_psbt.c | 64 +++++++++ 4 files changed, 369 insertions(+) create mode 100644 src/ctest/psbts.h create mode 100644 src/ctest/test_psbt.c 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/src/Makefile.am b/src/Makefile.am index f69b4cefe..df273f054 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -225,6 +225,11 @@ 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@ 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..37f6f4733 --- /dev/null +++ b/src/ctest/test_psbt.c @@ -0,0 +1,64 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "psbts.h" + +int main(void) +{ + size_t i; + + for (i = 0; i < sizeof(invalid_psbts) / sizeof(invalid_psbts[0]); i++) { + struct wally_psbt *psbt; + + if (wally_psbt_from_base64(invalid_psbts[i].base64, &psbt) != WALLY_OK) + 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; + + 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); + } + 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); + } + + return 0; +} From 7ab3ecc4ec22f16640e22135c45c74311bfb9ae1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:42:09 +0930 Subject: [PATCH 03/37] psbt: close some leaks. As revealed by: make && LD_LIBRARY_PATH=`pwd`/src/.libs valgrind --leak-check=full src/.libs/test_psbt There are more, but the next patches rework that code. Signed-off-by: Rusty Russell --- src/psbt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index 34a5e0244..a2fece179 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -288,10 +288,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)); From c1eb205a2f3460e20c2c4d5d321d388df086684b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:43:09 +0930 Subject: [PATCH 04/37] psbt: improve constness. Signed-off-by: Rusty Russell --- src/psbt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psbt.c b/src/psbt.c index a2fece179..214a35f89 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -17,7 +17,7 @@ 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; } From 560c292231bbe8cd9497c3898d2bc2be215ae7a3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:27 +0930 Subject: [PATCH 05/37] pullpush: add routines for safe marshal / unmarshal. Manually checking for end of buffers is error prone: these pull & push variants are similar to those used by c-lightning. Importantly, if they fail you can still call them, meaning you can check whether it overflowed at the end of all the work. The push function also can tell you exactly how many bytes short you were. Signed-off-by: Rusty Russell --- src/Makefile.am | 1 + src/pullpush.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++++ src/pullpush.h | 91 +++++++++++++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 src/pullpush.c create mode 100644 src/pullpush.h diff --git a/src/Makefile.am b/src/Makefile.am index df273f054..3f2351e3a 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 \ 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 */ From 2c308849ad9eee039e24eb14797452b83eb68434 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:55 +0930 Subject: [PATCH 06/37] psbt: use push functions for converting input to bytes. This also means we return WALLY_EINVAL instead of corrupting the stack if they provide a witness output > 50 bytes. Signed-off-by: Rusty Russell --- src/psbt.c | 243 +++++++++++++++++++++++++++++------------------------ 1 file changed, 134 insertions(+), 109 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index 214a35f89..da58246cc 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -13,6 +13,7 @@ #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}; @@ -1808,164 +1809,189 @@ int wally_psbt_get_length( return WALLY_OK; } -static int psbt_input_to_bytes( - const struct wally_psbt_input *input, - unsigned char *bytes_out, size_t len, - size_t *bytes_written) +/* 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); +} + +/* Common case of pushing a type whose key is a pubkey */ +static void push_psbt_key_with_pubkey( + unsigned char **cursor, size_t *max, + uint64_t type, + const unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]) +{ + if (pubkey_is_compressed(pubkey)) { + push_psbt_key(cursor, max, type, pubkey, EC_PUBLIC_KEY_LEN); + } else { + push_psbt_key(cursor, max, type, pubkey, + EC_PUBLIC_KEY_UNCOMPRESSED_LEN); + } +} + +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; + + ret = wally_tx_get_length(tx, flags, &txlen); + if (ret != WALLY_OK) { + return ret; + } + + push_varint(cursor, max, txlen); + + /* FIXME: convert wally_tx to use push */ + p = push_bytes(cursor, max, NULL, txlen); + if (!p) { + /* We catch this in caller. */ + return WALLY_OK; + } + + return wally_tx_to_bytes(tx, flags, p, txlen, &txlen); +} + +static void push_witness_stack( + unsigned char **cursor, size_t *max, + const struct wally_tx_witness_stack *witness) +{ + size_t i; + + push_varint(cursor, max, witness->num_items); + for (i = 0; i < witness->num_items; ++i) { + push_varbuff(cursor, max, witness->items[i].witness, + witness->items[i].witness_len); + } +} + +static void push_keypath_item( + unsigned char **cursor, size_t *max, + uint64_t type, + const struct wally_keypath_item *item) +{ + size_t origin_len, i; + + push_psbt_key_with_pubkey(cursor, max, type, item->pubkey); + + origin_len = 4; /* Start with 4 bytes for fingerprint */ + origin_len += item->origin.path_len * sizeof(uint32_t); + push_varint(cursor, max, origin_len); + + push_bytes(cursor, max, item->origin.fingerprint, 4); + for (i = 0; i < item->origin.path_len; ++i) { + push_bytes(cursor, max, + &item->origin.path[i], sizeof(uint32_t)); + } +} + +static int 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); + 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 += 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); - 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; } @@ -2102,12 +2128,11 @@ int wally_psbt_to_bytes( /* 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; - ret = psbt_input_to_bytes(input, p, end - p, &input_len); + size_t max = end - p; + ret = push_psbt_input(&p, &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]; From ef215ab241cafcd27478d1614cad1bc7f724a847 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:55 +0930 Subject: [PATCH 07/37] psbt: make output_to_bytes use push. Signed-off-by: Rusty Russell --- src/psbt.c | 68 ++++++++++++++---------------------------------------- 1 file changed, 17 insertions(+), 51 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index da58246cc..5bcaa2933 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -1995,77 +1995,44 @@ static int push_psbt_input( 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; } @@ -2136,12 +2103,11 @@ int wally_psbt_to_bytes( } 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); + size_t max = end - p; + ret = push_psbt_output(&p, &max, output); if (ret != WALLY_OK) { return ret; } - p += output_len; } *bytes_written = p - bytes_out; return WALLY_OK; From 8e3079f0107f1f058dd4da8deb91e7977a12c2dc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 08/37] wally_psbt: make wally_psbt_to_bytes use push itself. As a special bonus, document that it now returns bytes_written as the entire length, if the error was caused by bytes_len being insufficient. This allows the caller to avoid calling bytes_len in the common case, if they want to. Signed-off-by: Rusty Russell --- include/wally_psbt.h | 5 +++- src/psbt.c | 63 +++++++++++++++----------------------------- 2 files changed, 25 insertions(+), 43 deletions(-) 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/psbt.c b/src/psbt.c index 5bcaa2933..fdc9ed6a5 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -2041,75 +2041,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 max = end - p; - ret = push_psbt_input(&p, &max, input); + ret = push_psbt_input(&cursor, &max, input); if (ret != WALLY_OK) { return ret; } } for (i = 0; i < psbt->num_outputs; ++i) { struct wally_psbt_output *output = &psbt->outputs[i]; - size_t max = end - p; - ret = push_psbt_output(&p, &max, output); + ret = push_psbt_output(&cursor, &max, output); if (ret != WALLY_OK) { return ret; } } - *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; } From 041faeb0fe9a20ed5b738c7f64c79d253773a469 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 09/37] psbt: make psbt_input_get_length use the same code as psbt_input_to_bytes. This avoid (recently buggy!) duplication. Signed-off-by: Rusty Russell --- src/psbt.c | 217 +---------------------------------------------------- 1 file changed, 4 insertions(+), 213 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index fdc9ed6a5..7641aedea 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -1587,226 +1587,17 @@ int wally_psbt_from_bytes( return ret; } -static int psbt_input_get_length( - const struct wally_psbt_input *input, - size_t *len) -{ - int ret; - size_t out, tx_len, i; - if (!len) { - return WALLY_EINVAL; - } - - *len = 0; - out = 0; - - /* Non witness utxo */ - if (input->non_witness_utxo) { - out += 2; /* Key len and one byte type */ - ret = wally_tx_get_length(input->non_witness_utxo, WALLY_TX_FLAG_USE_WITNESS, &tx_len); - if (ret != WALLY_OK) { - return ret; - } - out += varbuff_get_length(tx_len); - } - /* Witness utxo */ - if (input->witness_utxo) { - size_t wit_size = 0; - out += 2; /* Key len and one byte type */ - wit_size += sizeof(input->witness_utxo->satoshi); - wit_size += varbuff_get_length(input->witness_utxo->script_len); - out += varbuff_get_length(wit_size); - } - /* Partial sigs */ - if (input->partial_sigs) { - struct wally_partial_sigs_map *partial_sigs = input->partial_sigs; - for (i = 0; i < partial_sigs->num_items; ++i) { - struct wally_partial_sigs_item *item = &partial_sigs->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - out += varint_get_length(34); - out += 34; /* Compressed pubkey + 1 byte type */ - } else { - out += varint_get_length(66); - out += 66; /* Uncompressed pubkey + 1 byte type */ - } - out += varbuff_get_length(item->sig_len); - } - } - /* Sighash type */ - if (input->sighash_type > 0) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(sizeof(input->sighash_type)); - } - /* Redeem script */ - if (input->redeem_script) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(input->redeem_script_len); - } - /* Witness script */ - if (input->witness_script) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(input->witness_script_len); - } - /* Keypaths */ - if (input->keypaths) { - struct wally_keypath_map *keypaths = input->keypaths; - for (i = 0; i < keypaths->num_items; ++i) { - size_t origin_len; - struct wally_keypath_item *item = &keypaths->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - out += varint_get_length(34); - out += 34; /* Compressed pubkey + 1 byte type */ - } else { - out += varint_get_length(66); - out += 66; /* Uncompressed pubkey + 1 byte type */ - } - - origin_len = 4; /* Start with 4 bytes for fingerprint */ - origin_len += item->origin.path_len * sizeof(uint32_t); - out += varint_get_length(origin_len); - out += origin_len; - } - } - /* Final scriptSig */ - if (input->final_script_sig) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(input->final_script_sig_len); - } - /* Final scriptWitness */ - if (input->final_witness) { - struct wally_tx_witness_stack *witness = input->final_witness; - size_t wit_len = varint_get_length(witness->num_items); - out += 2; /* Key len and one byte type */ - out += varint_get_length(witness->num_items); - for (i = 0; i < witness->num_items; ++i) { - out += varbuff_get_length(witness->items[i].witness_len); - wit_len += varbuff_get_length(witness->items[i].witness_len); - } - out += varint_get_length(wit_len); - } - /* Unknowns */ - if (input->unknowns) { - for (i = 0; i < input->unknowns->num_items; ++i) { - struct wally_unknowns_item *unknown = &input->unknowns->items[i]; - out += varbuff_get_length(unknown->key_len); - out += varbuff_get_length(unknown->value_len); - } - } - - /* Separator */ - out += 1; - - *len = out; - return WALLY_OK; -} - -static int psbt_output_get_length( - const struct wally_psbt_output *output, - size_t *len) -{ - size_t out, i; - if (!len) { - return WALLY_EINVAL; - } - - *len = 0; - out = 0; - - /* Redeem script */ - if (output->redeem_script) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(output->redeem_script_len); - } - /* Witness script */ - if (output->witness_script) { - out += 2; /* Key len and one byte type */ - out += varbuff_get_length(output->witness_script_len); - } - /* Keypaths */ - if (output->keypaths) { - struct wally_keypath_map *keypaths = output->keypaths; - for (i = 0; i < keypaths->num_items; ++i) { - size_t origin_len; - struct wally_keypath_item *item = &keypaths->items[i]; - if (pubkey_is_compressed(item->pubkey)) { - out += varint_get_length(34); - out += 34; /* Compressed pubkey + 1 byte type */ - } else { - out += varint_get_length(66); - out += 66; /* Uncompressed pubkey + 1 byte type */ - } - - 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); - } - } - - /* Separator */ - out += 1; - - *len = out; - return WALLY_OK; -} - 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 = 5; /* Start with 5 byte magic */ - - /* Global tx */ - out += 2; - ret = wally_tx_get_length(psbt->tx, 0, &tx_len); - if (ret != WALLY_OK) { - return ret; - } - out += varbuff_get_length(tx_len); - - /* Global unknowns */ - if (psbt->unknowns) { - for (i = 0; i < psbt->unknowns->num_items; ++i) { - struct wally_unknowns_item *unknown = &psbt->unknowns->items[i]; - out += varbuff_get_length(unknown->key_len); - out += varbuff_get_length(unknown->value_len); - } - } - /* Separator */ - out += 1; - - /* 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; + ret = wally_psbt_to_bytes(psbt, NULL, 0, len); + if (ret == WALLY_EINVAL && *len != 0) { + return WALLY_OK; } - - *len = out; - return WALLY_OK; + return ret; } /* Literally a varbuff containing only type as a varint, then optional data */ From ce3badc69108608b50b9ffab8e435ecf2d1b96b0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 10/37] psbt: use pull_ functions to parse input section. Signed-off-by: Rusty Russell --- src/psbt.c | 380 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 236 insertions(+), 144 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index 7641aedea..31249d058 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -10,6 +10,7 @@ #include #include +#include #include "transaction_shared.h" #include "script_int.h" #include "script.h" @@ -832,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; @@ -1043,19 +1164,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) { @@ -1075,220 +1192,191 @@ 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; + pre_key = *cursor; + while ((key_len = pull_varlength(cursor, max)) != 0) { + const unsigned char *key, *val; + size_t val_max; - if (key_len == 0) { - found_sep = true; - break; - } - - /* 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); + 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; } - p += script_len; - /* amount length (8 bytes) + script CSUint + script length = value length */ - if (8 + script_len_len + script_len != value_len) { - return WALLY_EINVAL; - } + 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( @@ -1542,15 +1630,19 @@ 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) { + size_t max = end - p; + ret = pull_psbt_input(&p, &max, 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++; + if (p == NULL) { + ret = WALLY_EINVAL; + goto fail; + } } /* Make sure that the number of inputs matches the number of inputs in the transaction */ From 5922dfc51048c6a1c2a637345894e29c0c09c744 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 11/37] psbt: use pull_ functions to parse output section. Signed-off-by: Rusty Russell --- src/psbt.c | 141 ++++++++++++++++------------------------------------- 1 file changed, 43 insertions(+), 98 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index 31249d058..a6085d035 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -1379,19 +1379,15 @@ static int pull_psbt_input( 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) { @@ -1401,116 +1397,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 the key itself */ - key = p; - type = key[0]; - p += key_len; + /* Read key value */ + pre_key = *cursor; + while ((key_len = pull_varlength(cursor, max)) != 0) { + const unsigned char *key; - /* Pre-read the value length but don't increment for a sanity check */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) + /* Start parsing key */ + pull_subfield_start(cursor, max, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_OUT_REDEEM_SCRIPT: { if (result->redeem_script_len != 0) { 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( @@ -1652,15 +1594,18 @@ 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) { + size_t max = end - p; + ret = pull_psbt_output(&p, &max, counts->output_counts[i], + &result->outputs[i]); + result->num_outputs++; if (ret != WALLY_OK) { goto fail; } - p += bytes_read; - result->num_outputs++; + if (p == NULL) { + ret = WALLY_EINVAL; + goto fail; + } } /* Make sure that the number of outputs matches the number ot outputs in the transaction */ From 5ede661fc8b42b6bedae05796eb2aee484beac5b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 12/37] psbt: convert count_psbt_parts to use pull_ code. Signed-off-by: Rusty Russell --- src/psbt.c | 118 +++++++++++++++++++---------------------------------- 1 file changed, 43 insertions(+), 75 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index a6085d035..bf236c25b 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -992,13 +992,9 @@ static int count_psbt_parts( 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); @@ -1008,38 +1004,34 @@ static int count_psbt_parts( result->num_outputs = 0; /* Skip the magic */ - CHECK_BUF_BOUNDS(p, (size_t)5, bytes, bytes_len) - p += 5; + pull_skip(&bytes, &bytes_len, sizeof(WALLY_PSBT_MAGIC)); /* Go through globals and count */ - while (p < end) { - /* Read the key length */ - vl = varint_from_bytes(p, &key_len); - CHECK_BUF_BOUNDS(p, key_len + vl, bytes, bytes_len) - p += vl; - - if (key_len == 0) { - break; - } - - /* 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_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; @@ -1050,40 +1042,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; - } + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { + const unsigned char *key; - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; - - /* Read value length */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) - p += vl; + /* Start parsing key */ + pull_subfield_start(&bytes, &bytes_len, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_IN_NON_WITNESS_UTXO: case WALLY_PSBT_IN_WITNESS_UTXO: case WALLY_PSBT_IN_SIGHASH_TYPE: @@ -1102,39 +1081,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; - } + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { + const unsigned char *key; - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; - - /* Read value length */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) - p += vl; + /* Start parsing key */ + pull_subfield_start(&bytes, &bytes_len, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_OUT_REDEEM_SCRIPT: case WALLY_PSBT_OUT_WITNESS_SCRIPT: break; @@ -1146,12 +1112,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; } From c270741b486e00f0859c728b282f549c68c0efb3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 13/37] psbt: convert wally_psbt_from_bytes to use pull_code. Now we pull the magic before calling count_psbt_parts(), it no longer needs to skip over it. Signed-off-by: Rusty Russell --- src/psbt.c | 112 ++++++++++++++++++----------------------------------- 1 file changed, 37 insertions(+), 75 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index bf236c25b..8fb4a1627 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -981,12 +981,6 @@ 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, @@ -1003,9 +997,6 @@ static int count_psbt_parts( result->num_inputs = 0; result->num_outputs = 0; - /* Skip the magic */ - pull_skip(&bytes, &bytes_len, sizeof(WALLY_PSBT_MAGIC)); - /* Go through globals and count */ while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { const unsigned char *key; @@ -1428,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) { @@ -1464,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; - } - - /* Read the key itself */ - key = p; - type = key[0]; - p += key_len; + pre_key = bytes; + while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { + const unsigned char *key, *val; + size_t val_max; - /* Pre-read the value length but don't increment for a sanity check */ - vl = varint_from_bytes(p, &value_len); - CHECK_BUF_BOUNDS(p, value_len + vl, bytes, bytes_len) + /* Start parsing key */ + pull_subfield_start(&bytes, &bytes_len, key_len, &key, &key_len); /* Process based on type */ - switch (type) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_GLOBAL_UNSIGNED_TX: { - size_t j; 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; } @@ -1513,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,18 +1513,13 @@ int wally_psbt_from_bytes( /* Read inputs */ for (i = 0; i < counts->num_inputs; ++i) { - size_t max = end - p; - ret = pull_psbt_input(&p, &max, counts->input_counts[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; } - if (p == NULL) { - ret = WALLY_EINVAL; - goto fail; - } } /* Make sure that the number of inputs matches the number of inputs in the transaction */ @@ -1563,21 +1530,16 @@ int wally_psbt_from_bytes( /* Read outputs */ for (i = 0; i < counts->num_outputs; ++i) { - size_t max = end - p; - ret = pull_psbt_output(&p, &max, counts->output_counts[i], + ret = pull_psbt_output(&bytes, &bytes_len, counts->output_counts[i], &result->outputs[i]); result->num_outputs++; if (ret != WALLY_OK) { goto fail; } - if (p == NULL) { - ret = WALLY_EINVAL; - goto fail; - } } - /* 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; } From f4aa2cae56eeb97c411359f03d938ba4bd6aad02 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 14/37] psbt: add test for marshal/unmarshal limits. This currently fails (badly). Signed-off-by: Rusty Russell --- src/Makefile.am | 5 +++ src/ctest/test_psbt_limits.c | 85 ++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/ctest/test_psbt_limits.c diff --git a/src/Makefile.am b/src/Makefile.am index 3f2351e3a..ec91673b2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -231,6 +231,11 @@ 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/test_psbt_limits.c b/src/ctest/test_psbt_limits.c new file mode 100644 index 000000000..e86c585d5 --- /dev/null +++ b/src/ctest/test_psbt_limits.c @@ -0,0 +1,85 @@ +/* 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" + +/* 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; +} + +static void test_psbt(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. */ + if (wally_psbt_from_bytes(p + plen - i, i, &psbt) == WALLY_OK) + wally_psbt_free(psbt); + + /* Now try flipping each bit in last byte. */ + for (bit = 0; bit < 8; bit++) { + p[plen - 1] ^= (1 << bit); + if (wally_psbt_from_bytes(p + plen - i, i, &psbt) == WALLY_OK) + wally_psbt_free(psbt); + p[plen - 1] ^= (1 << bit); + } + } +} + +int main(void) +{ + size_t i; + size_t plen; + unsigned char *p = cliff(&plen); + + for (i = 0; i < sizeof(invalid_psbts) / sizeof(invalid_psbts[0]); i++) { + test_psbt(invalid_psbts + i, p, plen); + } + + for (i = 0; i < sizeof(valid_psbts) / sizeof(valid_psbts[0]); i++) { + test_psbt(valid_psbts + i, p, plen); + } + + return 0; +} From 4c65ef64d4d96033c5f339b119dc60cba2d5403b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:46:42 +0930 Subject: [PATCH 15/37] psbt: add limit testing to unmarshalling as well. Signed-off-by: Rusty Russell --- src/ctest/test_psbt_limits.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/ctest/test_psbt_limits.c b/src/ctest/test_psbt_limits.c index e86c585d5..8648b9f10 100644 --- a/src/ctest/test_psbt_limits.c +++ b/src/ctest/test_psbt_limits.c @@ -37,8 +37,9 @@ static unsigned char *cliff(size_t *size) return p; } -static void test_psbt(const struct psbt_test *test, - unsigned char *p, size_t plen) +/* 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; @@ -67,6 +68,27 @@ static void test_psbt(const struct psbt_test *test, } } +/* 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; + + 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); +} + int main(void) { size_t i; @@ -74,11 +96,12 @@ int main(void) unsigned char *p = cliff(&plen); for (i = 0; i < sizeof(invalid_psbts) / sizeof(invalid_psbts[0]); i++) { - test_psbt(invalid_psbts + i, p, plen); + test_psbt_read(invalid_psbts + i, p, plen); } for (i = 0; i < sizeof(valid_psbts) / sizeof(valid_psbts[0]); i++) { - test_psbt(valid_psbts + i, p, plen); + test_psbt_read(valid_psbts + i, p, plen); + test_psbt_write(valid_psbts + i, p, plen); } return 0; From 29418448bbe0a3cd3f1d33f557632d06d074099a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:47:12 +0930 Subject: [PATCH 16/37] psbt: add allocation tracking to tests. valgrind is better, but this is simple and finds current leaks. Signed-off-by: Rusty Russell --- src/ctest/test_psbt.c | 38 +++++++++++++++++++++++++++++++++--- src/ctest/test_psbt_limits.c | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/ctest/test_psbt.c b/src/ctest/test_psbt.c index 37f6f4733..1abd75675 100644 --- a/src/ctest/test_psbt.c +++ b/src/ctest/test_psbt.c @@ -10,15 +10,42 @@ #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; - if (wally_psbt_from_base64(invalid_psbts[i].base64, &psbt) != WALLY_OK) + 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); } @@ -28,6 +55,7 @@ int main(void) 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); } @@ -37,8 +65,7 @@ int main(void) if (strcmp(output, valid_psbts[i].base64) != 0) { errx(1, "psbt %s turned into %s?", valid_psbts[i].base64, output); } - free(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); } @@ -58,6 +85,11 @@ int main(void) 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 index 8648b9f10..a1d1f549a 100644 --- a/src/ctest/test_psbt_limits.c +++ b/src/ctest/test_psbt_limits.c @@ -17,6 +17,25 @@ #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) { @@ -55,14 +74,24 @@ static void test_psbt_read(const struct psbt_test *test, 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); } } @@ -75,6 +104,7 @@ static void test_psbt_write(const struct psbt_test *test, size_t i, written; struct wally_psbt *psbt; + mallocs = frees = 0; if (wally_psbt_from_base64(test->base64, &psbt) != WALLY_OK) abort(); @@ -87,6 +117,10 @@ static void test_psbt_write(const struct psbt_test *test, 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) @@ -95,6 +129,8 @@ int main(void) 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); } From 2bbdd3e8934d00877393725b681599a040064d53 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 29 May 2020 13:58:49 -0400 Subject: [PATCH 17/37] Check that pointers are non-null rather than length is non-zero Byte vectors in a PSBT could have a 0 length and thus result in duplicate keys in a PSBT as length 0 is also assumed that there is no vector for that field. Instead of checking length, check that the pointer is non-null. To ensure the pointers are non-null, malloc 1 byte. The length is still stored as 0. --- src/ctest/psbts.h | 16 ++++++++++++++++ src/psbt.c | 25 ++++++++++++++++++++----- src/test/data/psbt.json | 9 ++++++++- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/ctest/psbts.h b/src/ctest/psbts.h index 51864c0eb..510e9f96d 100644 --- a/src/ctest/psbts.h +++ b/src/ctest/psbts.h @@ -204,6 +204,22 @@ static const struct psbt_test invalid_psbts[] = { Base64 String: */ "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnQbVf4qHUa4A"}, + +/* The following are a bunch of PSBTs with duplicate 0 length scripts */ + { "70736274ff01002a02000000000140420f000000000017a9146e91b72d5593e7d4391e2ff44e91e985c31641f08700000000000100000100016a00", + "cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQAAAQABagA="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff000000000000010109ffffffffffffffff000101090000cafe000000000000", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJ//////////8AAQEJAADK/gAAAAAAAA=="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff000000000000010304addedfee0103040100000000", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQMErd7f7gEDBAEAAAAA"}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff0000000000000104000104016a00", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQQAAQQBagA="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff00000000000001010900e1f50500000000000105000105015100", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQUAAQUBUQA="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff0000000000000107000107015100", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA="}, + { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff00000000000001010900e1f50500000000000108010001080301015100", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA="}, }; /* The following are valid PSBTs: diff --git a/src/psbt.c b/src/psbt.c index 8fb4a1627..e16fb77ba 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -1247,7 +1247,7 @@ static int pull_psbt_input( break; } case WALLY_PSBT_IN_REDEEM_SCRIPT: { - if (result->redeem_script_len != 0) { + if (result->redeem_script) { return WALLY_EINVAL; /* Already have a redeem script */ } subfield_nomore_end(cursor, max, key, key_len); @@ -1257,10 +1257,13 @@ static int pull_psbt_input( cursor, max)) { return WALLY_ENOMEM; } + if (result->redeem_script_len == 0) { + result->redeem_script = wally_malloc(1); + } break; } case WALLY_PSBT_IN_WITNESS_SCRIPT: { - if (result->witness_script_len != 0) { + if (result->witness_script) { return WALLY_EINVAL; /* Already have a witness script */ } subfield_nomore_end(cursor, max, key, key_len); @@ -1270,6 +1273,9 @@ static int pull_psbt_input( cursor, max)) { return WALLY_ENOMEM; } + if (result->witness_script_len == 0) { + result->witness_script = wally_malloc(1); + } break; } case WALLY_PSBT_IN_BIP32_DERIVATION: { @@ -1280,7 +1286,7 @@ static int pull_psbt_input( break; } case WALLY_PSBT_IN_FINAL_SCRIPTSIG: { - if (result->final_script_sig_len != 0) { + if (result->final_script_sig) { return WALLY_EINVAL; /* Already have a scriptSig */ } subfield_nomore_end(cursor, max, key, key_len); @@ -1290,6 +1296,9 @@ static int pull_psbt_input( cursor, max)) { return WALLY_ENOMEM; } + if (result->final_script_sig_len == 0) { + result->final_script_sig = wally_malloc(1); + } break; } case WALLY_PSBT_IN_FINAL_SCRIPTWITNESS: { @@ -1367,7 +1376,7 @@ static int pull_psbt_output( /* Process based on type */ switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_OUT_REDEEM_SCRIPT: { - if (result->redeem_script_len != 0) { + if (result->redeem_script) { return WALLY_EINVAL; /* Already have a redeem script */ } subfield_nomore_end(cursor, max, key, key_len); @@ -1377,10 +1386,13 @@ static int pull_psbt_output( cursor, max)) { return WALLY_ENOMEM; } + if (result->redeem_script_len == 0) { + result->redeem_script = wally_malloc(1); + } break; } case WALLY_PSBT_OUT_WITNESS_SCRIPT: { - if (result->witness_script_len != 0) { + if (result->witness_script) { return WALLY_EINVAL; /* Already have a witness script */ } subfield_nomore_end(cursor, max, key, key_len); @@ -1390,6 +1402,9 @@ static int pull_psbt_output( cursor, max)) { return WALLY_ENOMEM; } + if (result->witness_script_len == 0) { + result->witness_script = wally_malloc(1); + } break; } case WALLY_PSBT_OUT_BIP32_DERIVATION: { diff --git a/src/test/data/psbt.json b/src/test/data/psbt.json index d17719b96..7dfb053d7 100644 --- a/src/test/data/psbt.json +++ b/src/test/data/psbt.json @@ -17,7 +17,14 @@ "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABB9oARzBEAiB0AYrUGACXuHMyPAAVcgs2hMyBI4kQSOfbzZtVrWecmQIgc9Npt0Dj61Pc76M4I8gHBRTKVafdlUTxV8FnkTJhEYwBSDBFAiEA9hA4swjcHahlo0hSdG8BV3KTQgjG0kRUOTzZm98iF3cCIAVuZ1pnWm0KArhbFOXikHTYolqbV2C+ooFvZhkQoAbqAUdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSrgABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEHIyIAIIwjUxc3Q7WV37Sge3K6jkLjeX2nTof+fZ10l+OyAokDAQjaBABHMEQCIGLrelVhB6fHP0WsSrWh3d9vcHX7EnWWmn84Pv/3hLyyAiAMBdu3Rw2/LwhVfdNWxzJcHtMJE+mWzThAlF2xIijaXwFHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gFHUiEDCJ3BDHrG21T5EymvYXMz2ziM6tDCMfcjN50bmQMLAtwhAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zUq4AIQIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1PtnuylhxDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", - "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A" + "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", + "cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQAAAQABagA=", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJ//////////8AAQEJAADK/gAAAAAAAA==", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQMErd7f7gEDBAEAAAAA", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQQAAQQBagA=", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQUAAQUBUQA=", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA=", + "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA=" ], "valid" : [ { From 73078a9f3634071f29158c172c2d3f41046463d7 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 29 May 2020 14:49:41 -0400 Subject: [PATCH 18/37] Implement psbt global version type --- include/wally_psbt.h | 5 +++++ src/ctest/psbts.h | 5 +++++ src/psbt.c | 34 ++++++++++++++++++++++++++++++++++ src/test/data/psbt.json | 1 + src/test/util.py | 3 ++- 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/include/wally_psbt.h b/include/wally_psbt.h index 825b6d49e..1769c9ae1 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -11,6 +11,7 @@ extern "C" { #define WALLY_PSBT_SEPARATOR 0x00 #define WALLY_PSBT_GLOBAL_UNSIGNED_TX 0x00 +#define WALLY_PSBT_GLOBAL_VERSION 0xFB #define WALLY_PSBT_IN_NON_WITNESS_UTXO 0x00 #define WALLY_PSBT_IN_WITNESS_UTXO 0x01 @@ -26,6 +27,9 @@ extern "C" { #define WALLY_PSBT_OUT_WITNESS_SCRIPT 0x01 #define WALLY_PSBT_OUT_BIP32_DERIVATION 0x02 +/* PSBT Version number */ +#define WALLY_PSBT_HIGHEST_VERSION 0 + #ifdef SWIG struct wally_key_origin_info; struct wally_keypath_map; @@ -122,6 +126,7 @@ struct wally_psbt { size_t num_outputs; size_t outputs_allocation_len; struct wally_unknowns_map *unknowns; + uint32_t version; }; #endif /* SWIG */ diff --git a/src/ctest/psbts.h b/src/ctest/psbts.h index 510e9f96d..375223386 100644 --- a/src/ctest/psbts.h +++ b/src/ctest/psbts.h @@ -220,6 +220,11 @@ static const struct psbt_test invalid_psbts[] = { "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA="}, { "70736274ff0100330100000001111111111111111111111111111111111111facade11111111111111111111117f00000000ffffffff00000000000001010900e1f50500000000000108010001080301015100", "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA="}, + +/* Unsupported version number */ + { "70736274ff01fb04010000000100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000", + "cHNidP8B+wQBAAAAAQB1AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAAAA/v///wLT3/UFAAAAABl2qRTQxZkDxbrChodg6Q/VIaRmWqdlIIisAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4ezLhMAAAEA/aUBAQAAAAABAomjxx6rTSDgNxu7pMxpj6KVyUY6+i45f4UzzLYvlWflAQAAABcWABS+GNFSqbASA52vPafeT1M0nuy5hf////+G+KpDpx3/FEiJOlMKcjfva0YIu7LdLQFx5jrsakiQtAEAAAAXFgAU/j6e8adF6XTZAsQ1WUOryzS9U1P/////AgDC6wsAAAAAGXapFIXP8Ql/2eAIuzSvcJxiGXs4l4pIiKxy/vhOLAAAABepFDOXJboh79Yqx1OpvNBn1semo50FhwJHMEQCICcSviLgJw85T1aDEdx8qaaJcLgCX907JAIp8H+KXzokAiABizjX3NMU5zTJJ2vW+0D2czJbxLqhRMgA0vLwLbJ2XAEhA9LhVnSUG61KmWNyy4fhhW02UmBtmFYv45xenn5BPyEFAkgwRQIhANErhS2F3Nlh0vX0q2YGVN9u7cx5TAwzzlzDCf+1/OWNAiBnM4qODhclwZf7GoivWfUeROQlWyAWfIaEAxwF0fJZKgEhAiO3K+7wll0Qvgd47+zWH8rG95pOoWk5M4BzRGT4TyqzAAAAAAAAAA=="}, + }; /* The following are valid PSBTs: diff --git a/src/psbt.c b/src/psbt.c index e16fb77ba..eb92d073c 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -741,6 +741,9 @@ int wally_psbt_init_alloc( return WALLY_ENOMEM; } + /* Version is always 0 */ + result->version = 0; + result->inputs = new_inputs; result->num_inputs = 0; result->inputs_allocation_len = inputs_allocation_len; @@ -1030,6 +1033,11 @@ static int count_psbt_parts( } break; } + case WALLY_PSBT_GLOBAL_VERSION: + pull_subfield_end(&bytes, &bytes_len, key, key_len); + /* Skip over value */ + pull_skip(&bytes, &bytes_len, pull_varint(&bytes, &bytes_len)); + break; /* Unknowns */ default: result->num_global_unknowns++; @@ -1502,6 +1510,25 @@ int wally_psbt_from_bytes( } break; } + case WALLY_PSBT_GLOBAL_VERSION: { + if (result->version > 0) { + ret = WALLY_EINVAL; /* Version already provided */ + goto fail; + } + subfield_nomore_end(&bytes, &bytes_len, key, key_len); + + /* Start parsing the value field. */ + pull_subfield_start(&bytes, &bytes_len, + pull_varint(&bytes, &bytes_len), + &val, &val_max); + result->version = pull_le32(&val, &val_max); + subfield_nomore_end(&bytes, &bytes_len, val, val_max); + if (result->version > WALLY_PSBT_HIGHEST_VERSION) { + ret = WALLY_EINVAL; /* Unsupported version number */ + goto fail; + } + break; + } /* Unknowns */ default: { ret = pull_unknown_key_value(&bytes, &bytes_len, pre_key, @@ -1826,6 +1853,13 @@ int wally_psbt_to_bytes( push_psbt_key(&cursor, &max, WALLY_PSBT_GLOBAL_UNSIGNED_TX, NULL, 0); push_length_and_tx(&cursor, &max, psbt->tx, 0); + /* version */ + if (psbt->version > 0) { + push_psbt_key(&cursor, &max, WALLY_PSBT_GLOBAL_VERSION, NULL, 0); + push_varint(&cursor, &max, sizeof(uint32_t)); + push_le32(&cursor, &max, psbt->version); + } + /* Unknowns */ if (psbt->unknowns) { for (i = 0; i < psbt->unknowns->num_items; ++i) { diff --git a/src/test/data/psbt.json b/src/test/data/psbt.json index 7dfb053d7..63b795e1c 100644 --- a/src/test/data/psbt.json +++ b/src/test/data/psbt.json @@ -18,6 +18,7 @@ "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wCAwABAAAAAAEAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAgAAFgAUYunpgv/zTdgjlhAxawkM0qO3R8sAAQAiACCHa62DLx0WgBXtQSMqnqZaGBXZ7xPA74dZ9ktbKyeKZQEBJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", "cHNidP8BAHMCAAAAATAa6YblFqHsisW0vGVz0y+DtGXiOtdhZ9aLOOcwtNvbAAAAAAD/////AnR7AQAAAAAAF6kUA6oXrogrXQ1Usl1jEE5P/s57nqKHYEOZOwAAAAAXqRS5IbG6b3IuS/qDtlV6MTmYakLsg4cAAAAAAAEBHwDKmjsAAAAAFgAU0tlLZK4IWH7vyO6xh8YB6Tn5A3wAAQAWABRi6emC//NN2COWEDFrCQzSo7dHywABACIAIIdrrYMvHRaAFe1BIyqeploYFdnvE8Dvh1n2S1srJ4plIQEAJVEhA7fOI6AcW0vwCmQlN836uzFbZoMyhnR471EwnSvVf4qHUa4A", + "cHNidP8B+wQBAAAAAQB1AgAAAAEmgXE3Ht/yhek3re6ks3t4AAwFZsuzrWRkFxPKQhcb9gAAAAAA/v///wLT3/UFAAAAABl2qRTQxZkDxbrChodg6Q/VIaRmWqdlIIisAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4ezLhMAAAEA/aUBAQAAAAABAomjxx6rTSDgNxu7pMxpj6KVyUY6+i45f4UzzLYvlWflAQAAABcWABS+GNFSqbASA52vPafeT1M0nuy5hf////+G+KpDpx3/FEiJOlMKcjfva0YIu7LdLQFx5jrsakiQtAEAAAAXFgAU/j6e8adF6XTZAsQ1WUOryzS9U1P/////AgDC6wsAAAAAGXapFIXP8Ql/2eAIuzSvcJxiGXs4l4pIiKxy/vhOLAAAABepFDOXJboh79Yqx1OpvNBn1semo50FhwJHMEQCICcSviLgJw85T1aDEdx8qaaJcLgCX907JAIp8H+KXzokAiABizjX3NMU5zTJJ2vW+0D2czJbxLqhRMgA0vLwLbJ2XAEhA9LhVnSUG61KmWNyy4fhhW02UmBtmFYv45xenn5BPyEFAkgwRQIhANErhS2F3Nlh0vX0q2YGVN9u7cx5TAwzzlzDCf+1/OWNAiBnM4qODhclwZf7GoivWfUeROQlWyAWfIaEAxwF0fJZKgEhAiO3K+7wll0Qvgd47+zWH8rG95pOoWk5M4BzRGT4TyqzAAAAAAAAAA==", "cHNidP8BACoCAAAAAAFAQg8AAAAAABepFG6Rty1Vk+fUOR4v9E6R6YXDFkHwhwAAAAAAAQAAAQABagA=", "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJ//////////8AAQEJAADK/gAAAAAAAA==", "cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQMErd7f7gEDBAEAAAAA", diff --git a/src/test/util.py b/src/test/util.py index 0b197e77f..347f43756 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -178,7 +178,8 @@ class wally_psbt(Structure): ('outputs', POINTER(wally_psbt_output)), ('num_outputs', c_ulong), ('outputs_allocation_len', c_ulong), - ('unknowns', POINTER(unknowns_map))] + ('unknowns', POINTER(unknowns_map)), + ('version', c_ulong)] for f in ( ('wally_init', c_int, [c_uint]), From 4b370fd313619c2016e7c4cc6b381b6e4dd492e9 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 14 May 2020 19:51:03 -0400 Subject: [PATCH 19/37] Add wally_psbt_elements_input_init_alloc and elements fields setters --- include/wally_psbt.h | 190 +++++++++++++++++++++++++++++++++++++ src/psbt.c | 220 +++++++++++++++++++++++++++++++++++++++++++ src/test/util.py | 17 +++- 3 files changed, 426 insertions(+), 1 deletion(-) diff --git a/include/wally_psbt.h b/include/wally_psbt.h index 1769c9ae1..1efba7ffe 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -104,6 +104,24 @@ struct wally_psbt_input { struct wally_partial_sigs_map *partial_sigs; struct wally_unknowns_map *unknowns; uint32_t sighash_type; + +#ifdef BUILD_ELEMENTS + uint64_t value; + bool has_value; + unsigned char *value_blinder; + size_t value_blinder_len; + unsigned char *asset; + size_t asset_len; + unsigned char *asset_blinder; + size_t asset_blinder_len; + struct wally_tx *peg_in_tx; + unsigned char *txout_proof; + size_t txout_proof_len; + unsigned char *genesis_hash; + size_t genesis_hash_len; + unsigned char *claim_script; + size_t claim_script_len; +#endif /* BUILD_ELEMENTS */ }; /** A psbt output map */ @@ -586,6 +604,178 @@ WALLY_CORE_API int wally_extract_psbt( struct wally_psbt *psbt, struct wally_tx **output); +#ifdef BUILD_ELEMENTS +/** + * Allocate and initialize a new elements psbt. + * + * :param inputs_allocation_len: The number of inputs to pre-allocate space for. + * :param outputs_allocation_len: The number of outputs to pre-allocate space for. + * :param global_unknowns_allocation_len: The number of global unknowns to allocate space for. + * :param output: Destination for the resulting psbt output. + */ +WALLY_CORE_API int wally_psbt_elements_init_alloc( + size_t inputs_allocation_len, + size_t outputs_allocation_len, + size_t global_unknowns_allocation_len, + struct wally_psbt **output); + +/** + * Allocate and initialize a new psbt elements input. + * + * :param non_witness_utxo: The non witness utxo for this input if it exists. + * :param witness_utxo: The witness utxo for this input if it exists. + * :param redeem_script: The redeem script for this input + * :param redeem_script_len: The length of the redeem script. + * :param witness_script: The witness script for this input + * :param witness_script_len: The length of the witness script. + * :param final_script_sig: The scriptSig for this input + * :param final_script_sig_len: Size of ``final_script_sig`` in bytes. + * :param final_witness: The witness stack for the input, or NULL if no witness is present. + * :param keypaths: The HD keypaths for this input. + * :param partial_sigs: The partial signatures for this input. + * :param unknowns: The unknown key value pairs for this input. + * :param sighash_type: The sighash type for this input + * :param value: The value for this input + * :param value_blinder: The value blinder for ths input + * :param value_blinder_len: The length of the value_blinder. + * :param asset: The witness script for this input + * :param asset_len: The length of the witness script. + * :param asset_blinder: The witness script for this input + * :param asset_blinder__len: The length of the witness script. + * :param peg_in_tx: The witness script for this input + * :param txout_proof: The witness script for this input + * :param txout_proof_len: The length of the witness script. + * :param genesis_hash: The witness script for this input + * :param genesis_hash_len: The length of the witness script. + * :param claim_script: The witness script for this input + * :param claim_script_len: The length of the witness script. + * :param output: Destination for the resulting psbt input. + */ +WALLY_CORE_API int wally_psbt_elements_input_init_alloc( + struct wally_tx *non_witness_utxo, + struct wally_tx_output *witness_utxo, + unsigned char *redeem_script, + size_t redeem_script_len, + unsigned char *witness_script, + size_t witness_script_len, + unsigned char *final_script_sig, + size_t final_script_sig_len, + struct wally_tx_witness_stack *final_witness, + struct wally_keypath_map *keypaths, + struct wally_partial_sigs_map *partial_sigs, + struct wally_unknowns_map *unknowns, + uint32_t sighash_type, + uint64_t value, + bool has_value, + unsigned char *value_blinder, + size_t value_blinder_len, + unsigned char *asset, + size_t asset_len, + unsigned char *asset_blinder, + size_t asset_blinder_len, + struct wally_tx *peg_in_tx, + unsigned char *txout_proof, + size_t txout_proof_len, + unsigned char *genesis_hash, + size_t genesis_hash_len, + unsigned char *claim_script, + size_t claim_script_len, + struct wally_psbt_input **output); + +/** + * Set the value in an elements input + * + * :param input: The input to update. + * :param value: The value for this input + */ +WALLY_CORE_API int wally_psbt_elements_input_set_value( + struct wally_psbt_input *input, + uint64_t value); + +/** + * Set the value blinder in an elements input + * + * :param input: The input to update. + * :param value_blinder: The value blinder for this input + * :param value_blinder_len: The length of the value blinder. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_value_blinder( + struct wally_psbt_input *input, + unsigned char *value_blinder, + size_t value_blinder_len); + +/** + * Set the asset in an elements input + * + * :param input: The input to update. + * :param asset: The asset for this input + * :param asset_len: The length of the asset + */ +WALLY_CORE_API int wally_psbt_elements_input_set_asset( + struct wally_psbt_input *input, + unsigned char *asset, + size_t asset_len); + +/** + * Set the asset blinder in an elements input + * + * :param input: The input to update. + * :param asset_blinder: The asset blinder for this input + * :param asset_blinder_len: The length of the asset blinder. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_asset_blinder( + struct wally_psbt_input *input, + unsigned char *asset_blinder, + size_t asset_blinder_len); + +/** + * Set the peg in tx in an input + * + * :param input: The input to update. + * :param peg_in_tx: The peg in tx for this input if it exists. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_peg_in_tx( + struct wally_psbt_input *input, + struct wally_tx *peg_in_tx); + +/** + * Set the txout proof in an elements input + * + * :param input: The input to update. + * :param txout_proof: The txout proof for this input + * :param txout_proof_len: The length of the txout proof. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_txout_proof( + struct wally_psbt_input *input, + unsigned char *txout_proof, + size_t txout_proof_len); + +/** + * Set the genesis hash in an elements input + * + * :param input: The input to update. + * :param genesis_hash: The genesis hash for this input + * :param genesis_hash_len: The length of the genesis hash. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_genesis_hash( + struct wally_psbt_input *input, + unsigned char *genesis_hash, + size_t genesis_hash_len); + +/** + * Set the claim script in an elements input + * + * :param input: The input to update. + * :param claim_script: The claim script for this input + * :param claim_script_len: The length of the claim_script. + */ +WALLY_CORE_API int wally_psbt_elements_input_set_claim_script( + struct wally_psbt_input *input, + unsigned char *claim_script, + size_t claim_script_len); + +#endif /* BUILD_ELEMENTS */ + #ifdef __cplusplus } #endif diff --git a/src/psbt.c b/src/psbt.c index eb92d073c..045616f4d 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -443,6 +443,94 @@ int wally_psbt_input_init_alloc( return ret; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_input_init_alloc( + struct wally_tx *non_witness_utxo, + struct wally_tx_output *witness_utxo, + unsigned char *redeem_script, + size_t redeem_script_len, + unsigned char *witness_script, + size_t witness_script_len, + unsigned char *final_script_sig, + size_t final_script_sig_len, + struct wally_tx_witness_stack *final_witness, + struct wally_keypath_map *keypaths, + struct wally_partial_sigs_map *partial_sigs, + struct wally_unknowns_map *unknowns, + uint32_t sighash_type, + uint64_t value, + bool has_value, + unsigned char *value_blinder, + size_t value_blinder_len, + unsigned char *asset, + size_t asset_len, + unsigned char *asset_blinder, + size_t asset_blinder_len, + struct wally_tx *peg_in_tx, + unsigned char *txout_proof, + size_t txout_proof_len, + unsigned char *genesis_hash, + size_t genesis_hash_len, + unsigned char *claim_script, + size_t claim_script_len, + struct wally_psbt_input **output) +{ + struct wally_psbt_input *result; + int ret = wally_psbt_input_init_alloc(non_witness_utxo, + witness_utxo, + redeem_script, + redeem_script_len, + witness_script, + witness_script_len, + final_script_sig, + final_script_sig_len, + final_witness, + keypaths, + partial_sigs, + unknowns, + sighash_type, + output); + if (ret != WALLY_OK) { + return ret; + } + + /* wally_psbt_input_init_alloc will allocate and initialize the input */ + result = *output; + + if (has_value && (ret = wally_psbt_elements_input_set_value(result, value)) != WALLY_OK) { + goto fail; + } + if (value_blinder && (ret = wally_psbt_elements_input_set_value_blinder(result, value_blinder, value_blinder_len)) != WALLY_OK) { + goto fail; + } + if (asset && (ret = wally_psbt_elements_input_set_asset(result, asset, asset_len)) != WALLY_OK) { + goto fail; + } + if (asset_blinder && (ret = wally_psbt_elements_input_set_asset_blinder(result, asset_blinder, asset_blinder_len)) != WALLY_OK) { + goto fail; + } + if (peg_in_tx && (ret = wally_psbt_elements_input_set_peg_in_tx(result, peg_in_tx)) != WALLY_OK) { + goto fail; + } + if (txout_proof && (ret = wally_psbt_elements_input_set_txout_proof(result, txout_proof, txout_proof_len)) != WALLY_OK) { + goto fail; + } + if (genesis_hash && (ret = wally_psbt_elements_input_set_genesis_hash(result, genesis_hash, genesis_hash_len)) != WALLY_OK) { + goto fail; + } + if (claim_script && (ret = wally_psbt_elements_input_set_claim_script(result, claim_script, claim_script_len)) != WALLY_OK) { + goto fail; + } + + return ret; + +fail: + wally_psbt_input_free(result); + *output = NULL; + return ret; +} +#endif /* BUILD_ELEMENTS */ + int wally_psbt_input_set_non_witness_utxo( struct wally_psbt_input *input, struct wally_tx *non_witness_utxo) @@ -585,6 +673,129 @@ int wally_psbt_input_set_sighash_type( return WALLY_OK; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_input_set_value( + struct wally_psbt_input *input, + uint64_t value) +{ + input->value = value; + input->has_value = true; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_value_blinder( + struct wally_psbt_input *input, + unsigned char *value_blinder, + size_t value_blinder_len) +{ + unsigned char *result_value_blinder; + + if (!clone_bytes(&result_value_blinder, value_blinder, value_blinder_len)) { + return WALLY_ENOMEM; + } + wally_free(input->value_blinder); + input->value_blinder = value_blinder; + input->value_blinder_len = value_blinder_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_asset( + struct wally_psbt_input *input, + unsigned char *asset, + size_t asset_len) +{ + unsigned char *result_asset; + + if (!clone_bytes(&result_asset, asset, asset_len)) { + return WALLY_ENOMEM; + } + wally_free(input->asset); + input->asset = asset; + input->asset_len = asset_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_asset_blinder( + struct wally_psbt_input *input, + unsigned char *asset_blinder, + size_t asset_blinder_len) +{ + unsigned char *result_asset_blinder; + + if (!clone_bytes(&result_asset_blinder, asset_blinder, asset_blinder_len)) { + return WALLY_ENOMEM; + } + wally_free(input->asset_blinder); + input->asset_blinder = asset_blinder; + input->asset_blinder_len = asset_blinder_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_peg_in_tx( + struct wally_psbt_input *input, + struct wally_tx *peg_in_tx) +{ + int ret = WALLY_OK; + struct wally_tx *result_peg_in_tx; + + if ((ret = clone_tx(peg_in_tx, &result_peg_in_tx)) != WALLY_OK) { + return ret; + } + wally_tx_free(input->peg_in_tx); + input->peg_in_tx = peg_in_tx; + return ret; +} + + +int wally_psbt_elements_input_set_txout_proof( + struct wally_psbt_input *input, + unsigned char *txout_proof, + size_t txout_proof_len) +{ + unsigned char *result_txout_proof; + + if (!clone_bytes(&result_txout_proof, txout_proof, txout_proof_len)) { + return WALLY_ENOMEM; + } + wally_free(input->txout_proof); + input->txout_proof = txout_proof; + input->txout_proof_len = txout_proof_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_genesis_hash( + struct wally_psbt_input *input, + unsigned char *genesis_hash, + size_t genesis_hash_len) +{ + unsigned char *result_genesis_hash; + + if (!clone_bytes(&result_genesis_hash, genesis_hash, genesis_hash_len)) { + return WALLY_ENOMEM; + } + wally_free(input->genesis_hash); + input->genesis_hash = genesis_hash; + input->genesis_hash_len = genesis_hash_len; + return WALLY_OK; +} + +int wally_psbt_elements_input_set_claim_script( + struct wally_psbt_input *input, + unsigned char *claim_script, + size_t claim_script_len) +{ + unsigned char *result_claim_script; + + if (!clone_bytes(&result_claim_script, claim_script, claim_script_len)) { + return WALLY_ENOMEM; + } + wally_free(input->claim_script); + input->claim_script = claim_script; + input->claim_script_len = claim_script_len; + return WALLY_OK; +} +#endif /* BUILD_ELEMENTS */ + int wally_psbt_input_free(struct wally_psbt_input *input) { if (input) { @@ -597,6 +808,15 @@ int wally_psbt_input_free(struct wally_psbt_input *input) wally_keypath_map_free(input->keypaths); wally_partial_sigs_map_free(input->partial_sigs); wally_unknowns_map_free(input->unknowns); +#ifdef BUILD_ELEMENTS + clear_and_free(input->value_blinder, input->value_blinder_len); + clear_and_free(input->asset, input->asset_len); + clear_and_free(input->asset_blinder, input->asset_blinder_len); + wally_tx_free(input->peg_in_tx); + clear_and_free(input->txout_proof, input->txout_proof_len); + clear_and_free(input->genesis_hash, input->genesis_hash_len); + clear_and_free(input->claim_script, input->claim_script_len); +#endif /* BUILD_ELEMENTS */ } return WALLY_OK; } diff --git a/src/test/util.py b/src/test/util.py index 347f43756..bf48d3e0a 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -160,7 +160,22 @@ class wally_psbt_input(Structure): ('keypaths', POINTER(keypath_map)), ('partial_sigs', POINTER(partial_sigs_map)), ('unknowns', POINTER(unknowns_map)), - ('sighash_type', c_ulong)] + ('sighash_type', c_ulong), + ('value', c_ulonglong), + ('has_value', c_ulong), + ('value_blinder', c_void_p), + ('value_blinder_len', c_ulong), + ('asset', c_void_p), + ('asset_len', c_ulong), + ('asset_blinder', c_void_p), + ('asset_blinder_len', c_ulong), + ('peg_in_tx', POINTER(wally_tx)), + ('txout_proof', c_void_p), + ('txout_proof_len', c_ulong), + ('genesis_hash', c_void_p), + ('genesis_hash_len', c_ulong), + ('claim_script', c_void_p), + ('claim_script_len', c_ulong)] class wally_psbt_output(Structure): _fields_ = [('redeem_script', c_void_p), From 6e2b6a232ad6c4b3d4f5563f6946f7c31736f36d Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Thu, 14 May 2020 20:46:20 -0400 Subject: [PATCH 20/37] Add wally_psbt_elements_output_init_alloc and field setters --- include/wally_psbt.h | 163 ++++++++++++++++++++++++++++++++++ src/psbt.c | 207 +++++++++++++++++++++++++++++++++++++++++++ src/test/util.py | 18 +++- 3 files changed, 387 insertions(+), 1 deletion(-) diff --git a/include/wally_psbt.h b/include/wally_psbt.h index 1efba7ffe..e15ab2c34 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -132,6 +132,24 @@ struct wally_psbt_output { size_t witness_script_len; struct wally_keypath_map *keypaths; struct wally_unknowns_map *unknowns; +#ifdef BUILD_ELEMENTS + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]; + bool has_blinding_pubkey; + unsigned char *value_commitment; + size_t value_commitment_len; + unsigned char *value_blinder; + size_t value_blinder_len; + unsigned char *asset_commitment; + size_t asset_commitment_len; + unsigned char *asset_blinder; + size_t asset_blinder_len; + unsigned char *nonce_commitment; + size_t nonce_commitment_len; + unsigned char *range_proof; + size_t range_proof_len; + unsigned char *surjection_proof; + size_t surjection_proof_len; +#endif /* BUILD_ELEMENTS */ }; /** A partially signed bitcoin transaction */ @@ -774,6 +792,151 @@ WALLY_CORE_API int wally_psbt_elements_input_set_claim_script( unsigned char *claim_script, size_t claim_script_len); +/** + * Allocate and initialize a new psbt elements output. + * + * :param redeem_script: The redeem script needed for spending this output + * :param redeem_script_len: The length of the redeem script. + * :param witness_script: The witness script needed for spending for this output + * :param witness_script_len: The length of the witness script. + * :param keypaths: The HD keypaths for the keys needed for spending this output + * :param unknowns: The unknown key value pairs for this output. + * :param blinding_pubkey: The blinding pubkey for this output + * :param value_commitment: The value commitment for this output + * :param value_commitment_len: The length of the witness script. + * :param value_blinder: The value blinder for this output + * :param value_blinder_len: The length of the value blinder. + * :param asset_commitment: The asset commitment for this output + * :param asset_commitment_len: The length of the asset commitment. + * :param asset_blinder: The asset blinder for this output + * :param asset_blinder_len: The length of the asset blinder. + * :param nonce_commitment: The nonce commitment for this output + * :param nonce_commitment_len: The length of the nonce commitment. + * :param range_proof: The range proof for this output + * :param range_proof_len: The length of the range proof. + * :param surjection_proof: The surjection proof for this output + * :param surjection_proof_len: The length of the surjection proof. + * :param output: Destination for the resulting psbt output. + */ +WALLY_CORE_API int wally_psbt_elements_output_init_alloc( + unsigned char *redeem_script, + size_t redeem_script_len, + unsigned char *witness_script, + size_t witness_script_len, + struct wally_keypath_map *keypaths, + struct wally_unknowns_map *unknowns, + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN], + bool has_blinding_pubkey, + unsigned char *value_commitment, + size_t value_commitment_len, + unsigned char *value_blinder, + size_t value_blinder_len, + unsigned char *asset_commitment, + size_t asset_commitment_len, + unsigned char *asset_blinder, + size_t asset_blinder_len, + unsigned char *nonce_commitment, + size_t nonce_commitment_len, + unsigned char *range_proof, + size_t range_proof_len, + unsigned char *surjection_proof, + size_t surjection_proof_len, + struct wally_psbt_output **output); + +/** + * Set the blinding pubkey in an elements output + * + * :param output: The output to update. + * :param blinding_pubkey: The blinding pubkey for this output + */ +WALLY_CORE_API int wally_psbt_elements_output_set_blinding_pubkey( + struct wally_psbt_output *output, + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]); + +/** + * Set the value commitment in an elements output + * + * :param output: The output to update. + * :param value_commitment: The value commitment for this output + * :param value_commitment_len: The length of the value commitment. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_value_commitment( + struct wally_psbt_output *output, + unsigned char *value_commitment, + size_t value_commitment_len); + +/** + * Set the value blinder in an elements output + * + * :param output: The output to update. + * :param value_blinder: The value blinder for this output + * :param value_blinder_len: The length of the value blinder. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_value_blinder( + struct wally_psbt_output *output, + unsigned char *value_blinder, + size_t value_blinder_len); + +/** + * Set the asset commitment in an elements output + * + * :param output: The output to update. + * :param asset_commitment: The asset commitment for this output + * :param asset_commitment_len: The length of the asset commitment. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_asset_commitment( + struct wally_psbt_output *output, + unsigned char *asset_commitment, + size_t asset_commitment_len); + +/** + * Set the asset blinder in an elements output + * + * :param output: The output to update. + * :param asset_blinder: The asset blinder for this output + * :param asset_blinder_len: The length of the asset blinder. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_asset_blinder( + struct wally_psbt_output *output, + unsigned char *asset_blinder, + size_t asset_blinder_len); + +/** + * Set the nonce commitment in an elements output + * + * :param output: The output to update. + * :param nonce_commitment: The nonce commitment for this output + * :param nonce_commitment_len: The length of the nonce commitment. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_nonce_commitment( + struct wally_psbt_output *output, + unsigned char *nonce_commitment, + size_t nonce_commitment_len); + +/** + * Set the range proof in an elements output + * + * :param output: The output to update. + * :param range_proof: The range_proof for this output + * :param range_proof_len: The length of the raange proof. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_range_proof( + struct wally_psbt_output *output, + unsigned char *range_proof, + size_t range_proof_len); + +/** + * Set the surjection proof in an elements output + * + * :param output: The output to update. + * :param surjection_proof: The surjection proof for this output + * :param surjection_proof: The length of the surjection proof. + */ +WALLY_CORE_API int wally_psbt_elements_output_set_surjection_proof( + struct wally_psbt_output *output, + unsigned char *surjection_proof, + size_t surjection_proof_len); + #endif /* BUILD_ELEMENTS */ #ifdef __cplusplus diff --git a/src/psbt.c b/src/psbt.c index 045616f4d..f45b3888e 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -857,6 +857,81 @@ int wally_psbt_output_init_alloc( return ret; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_output_init_alloc( + unsigned char *redeem_script, + size_t redeem_script_len, + unsigned char *witness_script, + size_t witness_script_len, + struct wally_keypath_map *keypaths, + struct wally_unknowns_map *unknowns, + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN], + bool has_blinding_pubkey, + unsigned char *value_commitment, + size_t value_commitment_len, + unsigned char *value_blinder, + size_t value_blinder_len, + unsigned char *asset_commitment, + size_t asset_commitment_len, + unsigned char *asset_blinder, + size_t asset_blinder_len, + unsigned char *nonce_commitment, + size_t nonce_commitment_len, + unsigned char *range_proof, + size_t range_proof_len, + unsigned char *surjection_proof, + size_t surjection_proof_len, + struct wally_psbt_output **output) +{ + struct wally_psbt_output *result; + int ret = wally_psbt_output_init_alloc(redeem_script, + redeem_script_len, + witness_script, + witness_script_len, + keypaths, + unknowns, + output); + if (ret != WALLY_OK) { + return ret; + } + + /* wally_psbt_output_init_alloc allocates for us */ + result = *output; + + if (has_blinding_pubkey && (ret = wally_psbt_elements_output_set_blinding_pubkey(result, blinding_pubkey)) != WALLY_OK) { + goto fail; + } + if (value_commitment && (ret = wally_psbt_elements_output_set_value_commitment(result, value_commitment, value_commitment_len)) != WALLY_OK) { + goto fail; + } + if (value_blinder && (ret = wally_psbt_elements_output_set_value_blinder(result, value_blinder, value_blinder_len)) != WALLY_OK) { + goto fail; + } + if (asset_commitment && (ret = wally_psbt_elements_output_set_asset_commitment(result, asset_commitment, asset_commitment_len)) != WALLY_OK) { + goto fail; + } + if (asset_blinder && (ret = wally_psbt_elements_output_set_asset_blinder(result, asset_blinder, asset_blinder_len)) != WALLY_OK) { + goto fail; + } + if (nonce_commitment && (ret = wally_psbt_elements_output_set_nonce_commitment(result, nonce_commitment, nonce_commitment_len)) != WALLY_OK) { + goto fail; + } + if (range_proof && (ret = wally_psbt_elements_output_set_range_proof(result, range_proof, range_proof_len)) != WALLY_OK) { + goto fail; + } + if (surjection_proof && (ret = wally_psbt_elements_output_set_surjection_proof(result, surjection_proof, surjection_proof_len)) != WALLY_OK) { + goto fail; + } + + return ret; + +fail: + wally_psbt_output_free(result); + *output = NULL; + return ret; +} +#endif /* BUILD_ELEMENTS */ + int wally_psbt_output_set_redeem_script( struct wally_psbt_output *output, unsigned char *redeem_script, @@ -917,6 +992,129 @@ int wally_psbt_output_set_unknowns( return WALLY_OK; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_output_set_blinding_pubkey( + struct wally_psbt_output *output, + unsigned char blinding_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]) +{ + memcpy(output->blinding_pubkey, blinding_pubkey, EC_PUBLIC_KEY_UNCOMPRESSED_LEN); + output->has_blinding_pubkey = true; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_value_commitment( + struct wally_psbt_output *output, + unsigned char *value_commitment, + size_t value_commitment_len) +{ + unsigned char *result_value_commitment; + + if (!clone_bytes(&result_value_commitment, value_commitment, value_commitment_len)) { + return WALLY_ENOMEM; + } + wally_free(output->value_commitment); + output->value_commitment = value_commitment; + output->value_commitment_len = value_commitment_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_value_blinder( + struct wally_psbt_output *output, + unsigned char *value_blinder, + size_t value_blinder_len) +{ + unsigned char *result_value_blinder; + + if (!clone_bytes(&result_value_blinder, value_blinder, value_blinder_len)) { + return WALLY_ENOMEM; + } + wally_free(output->value_blinder); + output->value_blinder = value_blinder; + output->value_blinder_len = value_blinder_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_asset_commitment( + struct wally_psbt_output *output, + unsigned char *asset_commitment, + size_t asset_commitment_len) +{ + unsigned char *result_asset_commitment; + + if (!clone_bytes(&result_asset_commitment, asset_commitment, asset_commitment_len)) { + return WALLY_ENOMEM; + } + wally_free(output->asset_commitment); + output->asset_commitment = asset_commitment; + output->asset_commitment_len = asset_commitment_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_asset_blinder( + struct wally_psbt_output *output, + unsigned char *asset_blinder, + size_t asset_blinder_len) +{ + unsigned char *result_asset_blinder; + + if (!clone_bytes(&result_asset_blinder, asset_blinder, asset_blinder_len)) { + return WALLY_ENOMEM; + } + wally_free(output->asset_blinder); + output->asset_blinder = asset_blinder; + output->asset_blinder_len = asset_blinder_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_nonce_commitment( + struct wally_psbt_output *output, + unsigned char *nonce_commitment, + size_t nonce_commitment_len) +{ + unsigned char *result_nonce_commitment; + + if (!clone_bytes(&result_nonce_commitment, nonce_commitment, nonce_commitment_len)) { + return WALLY_ENOMEM; + } + wally_free(output->nonce_commitment); + output->nonce_commitment = nonce_commitment; + output->nonce_commitment_len = nonce_commitment_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_range_proof( + struct wally_psbt_output *output, + unsigned char *range_proof, + size_t range_proof_len) +{ + unsigned char *result_range_proof; + + if (!clone_bytes(&result_range_proof, range_proof, range_proof_len)) { + return WALLY_ENOMEM; + } + wally_free(output->range_proof); + output->range_proof = range_proof; + output->range_proof_len = range_proof_len; + return WALLY_OK; +} + +int wally_psbt_elements_output_set_surjection_proof( + struct wally_psbt_output *output, + unsigned char *surjection_proof, + size_t surjection_proof_len) +{ + unsigned char *result_surjection_proof; + + if (!clone_bytes(&result_surjection_proof, surjection_proof, surjection_proof_len)) { + return WALLY_ENOMEM; + } + wally_free(output->surjection_proof); + output->surjection_proof = surjection_proof; + output->surjection_proof_len = surjection_proof_len; + return WALLY_OK; +} +#endif/* BUILD_ELEMENTS */ + int wally_psbt_output_free(struct wally_psbt_output *output) { if (output) { @@ -924,6 +1122,15 @@ int wally_psbt_output_free(struct wally_psbt_output *output) clear_and_free(output->witness_script, output->witness_script_len); wally_keypath_map_free(output->keypaths); wally_unknowns_map_free(output->unknowns); +#ifdef BUILD_ELEMENTS + clear_and_free(output->value_commitment, output->value_commitment_len); + clear_and_free(output->value_blinder, output->value_blinder_len); + clear_and_free(output->asset_commitment, output->asset_commitment_len); + clear_and_free(output->asset_blinder, output->asset_blinder_len); + clear_and_free(output->nonce_commitment, output->nonce_commitment_len); + clear_and_free(output->range_proof, output->range_proof_len); + clear_and_free(output->surjection_proof, output->surjection_proof_len); +#endif /* BUILD_ELEMENTS */ } return WALLY_OK; } diff --git a/src/test/util.py b/src/test/util.py index bf48d3e0a..0e8061430 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -183,7 +183,23 @@ class wally_psbt_output(Structure): ('witness_script', c_void_p), ('witness_script_len', c_ulong), ('keypaths', POINTER(keypath_map)), - ('unknowns', POINTER(unknowns_map))] + ('unknowns', POINTER(unknowns_map)), + ('blinding_pubkey', c_ubyte * 65), + ('has_blinding_pubkey', c_ulong), + ('value_commitment', c_void_p), + ('value_commitment_len', c_ulong), + ('value_blinder', c_void_p), + ('value_blinder_len', c_ulong), + ('asset_commitment', c_void_p), + ('asset_commitment_len', c_ulong), + ('asset_blinder', c_void_p), + ('asset_blinder_len', c_ulong), + ('nonce_commitment', c_void_p), + ('nonce_commitment_len', c_ulong), + ('range_proof', c_void_p), + ('range_proof_len', c_ulong), + ('surjection_proof', c_void_p), + ('surjection_proof_len', c_ulong)] class wally_psbt(Structure): _fields_ = [('tx', POINTER(wally_tx)), From d83b39c52164ac56376abca397f4031996ded828 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 15 May 2020 12:47:29 -0400 Subject: [PATCH 21/37] Have the PSBT store it's magic to distinguish between psbt and pset wally_psbt_elements_init_alloc is added to set an elements magic wally_psbt_init_alloc, wally_psbt_to_bytes, and wally_psbt_from_bytes are updated to deal with this magic instead of the hard coded magic. wally_psbt_is_elements is added. --- include/wally_psbt.h | 11 ++++++++++ src/psbt.c | 49 +++++++++++++++++++++++++++++++++++++++++++- src/test/util.py | 3 ++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/include/wally_psbt.h b/include/wally_psbt.h index e15ab2c34..1197dd0b9 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -154,6 +154,7 @@ struct wally_psbt_output { /** A partially signed bitcoin transaction */ struct wally_psbt { + unsigned char magic[5]; struct wally_tx *tx; struct wally_psbt_input *inputs; size_t num_inputs; @@ -622,6 +623,16 @@ WALLY_CORE_API int wally_extract_psbt( struct wally_psbt *psbt, struct wally_tx **output); +/** + * Determine if a psbt is an elements psbt. + * + * :param psbt: The psbt to check. + * :param written: 1 if the transaction is an elements psbt, otherwise 0. + */ +WALLY_CORE_API int wally_psbt_is_elements( + const struct wally_psbt *psbt, + size_t *written); + #ifdef BUILD_ELEMENTS /** * Allocate and initialize a new elements psbt. diff --git a/src/psbt.c b/src/psbt.c index f45b3888e..0c179d1e4 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -18,6 +18,9 @@ const uint8_t WALLY_PSBT_MAGIC[5] = {'p', 's', 'b', 't', 0xff}; +#ifdef BUILD_ELEMENTS +const uint8_t WALLY_ELEMENTS_PSBT_MAGIC[5] = {'p', 's', 'e', 't', 0xff}; +#endif /* BUILD_ELEMENTS */ static bool pubkey_is_compressed(const unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]) { return pubkey[0] == 0x02 || pubkey[0] == 0x03; @@ -1171,6 +1174,8 @@ int wally_psbt_init_alloc( /* Version is always 0 */ result->version = 0; + memcpy(result->magic, WALLY_PSBT_MAGIC, 5); + result->inputs = new_inputs; result->num_inputs = 0; result->inputs_allocation_len = inputs_allocation_len; @@ -1181,6 +1186,22 @@ int wally_psbt_init_alloc( return WALLY_OK; } +#ifdef BUILD_ELEMENTS +int wally_psbt_elements_init_alloc( + size_t inputs_allocation_len, + size_t outputs_allocation_len, + size_t global_unknowns_allocation_len, + struct wally_psbt **output) +{ + int ret; + + ret = wally_psbt_init_alloc(inputs_allocation_len, outputs_allocation_len, global_unknowns_allocation_len, output); + memcpy((*output)->magic, WALLY_ELEMENTS_PSBT_MAGIC, 5); + + return ret; +} +#endif /* BUILD_ELEMENTS */ + int wally_psbt_free(struct wally_psbt *psbt) { size_t i; @@ -1883,8 +1904,15 @@ int wally_psbt_from_bytes( goto fail; } if (memcmp(magic, WALLY_PSBT_MAGIC, sizeof(WALLY_PSBT_MAGIC)) != 0 ) { +#ifdef BUILD_ELEMENTS + if (memcmp(magic, WALLY_ELEMENTS_PSBT_MAGIC, sizeof(WALLY_ELEMENTS_PSBT_MAGIC)) != 0) { + ret = WALLY_EINVAL; /* Invalid Magic */ + goto fail; + } +#else ret = WALLY_EINVAL; /* Invalid Magic */ goto fail; +#endif /* BUILD_ELEMENTS */ } /* Get a count of the psbt parts */ @@ -1900,6 +1928,9 @@ int wally_psbt_from_bytes( } *output = result; + /* Set the magic */ + memcpy(result->magic, magic, 5); + /* Read globals first */ pre_key = bytes; while ((key_len = pull_varlength(&bytes, &bytes_len)) != 0) { @@ -2274,7 +2305,7 @@ int wally_psbt_to_bytes( *bytes_written = 0; - push_bytes(&cursor, &max, WALLY_PSBT_MAGIC, sizeof(WALLY_PSBT_MAGIC)); + push_bytes(&cursor, &max, psbt->magic, sizeof(psbt->magic)); /* Global tx */ push_psbt_key(&cursor, &max, WALLY_PSBT_GLOBAL_UNSIGNED_TX, NULL, 0); @@ -3190,3 +3221,19 @@ int wally_extract_psbt( } return ret; } + +int wally_psbt_is_elements( + const struct wally_psbt *psbt, + size_t *written) +{ + if (!psbt || !written) + return WALLY_EINVAL; + + *written = 0; +#ifdef BUILD_ELEMENTS + if (memcmp(psbt->magic, WALLY_ELEMENTS_PSBT_MAGIC, 5) == 0) { + *written = 1; + } +#endif /* BUILD_ELEMENTS */ + return WALLY_OK; +} diff --git a/src/test/util.py b/src/test/util.py index 0e8061430..27463c3b3 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -202,7 +202,8 @@ class wally_psbt_output(Structure): ('surjection_proof_len', c_ulong)] class wally_psbt(Structure): - _fields_ = [('tx', POINTER(wally_tx)), + _fields_ = [('magic', c_ubyte * 5), + ('tx', POINTER(wally_tx)), ('inputs', POINTER(wally_psbt_input)), ('num_inputs', c_ulong), ('inputs_allocation_len', c_ulong), From 390324aaa8e6fa4d6e4d5334b11eb62446d9eac4 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 29 May 2020 21:21:18 -0400 Subject: [PATCH 22/37] Implement Elements psbt de/serialization as a proprietary type --- include/wally_psbt.h | 25 +++ src/psbt.c | 488 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 513 insertions(+) diff --git a/include/wally_psbt.h b/include/wally_psbt.h index 1197dd0b9..78d7ae936 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -10,6 +10,9 @@ extern "C" { #define WALLY_PSBT_SEPARATOR 0x00 +/* The proprietary type is the same for all scopes */ +#define WALLY_PSBT_PROPRIETARY_TYPE 0xFC + #define WALLY_PSBT_GLOBAL_UNSIGNED_TX 0x00 #define WALLY_PSBT_GLOBAL_VERSION 0xFB @@ -23,10 +26,32 @@ extern "C" { #define WALLY_PSBT_IN_FINAL_SCRIPTSIG 0x07 #define WALLY_PSBT_IN_FINAL_SCRIPTWITNESS 0x08 +#ifdef BUILD_ELEMENTS +#define WALLY_PSBT_IN_ELEMENTS_VALUE 0x00 +#define WALLY_PSBT_IN_ELEMENTS_VALUE_BLINDER 0x01 +#define WALLY_PSBT_IN_ELEMENTS_ASSET 0x02 +#define WALLY_PSBT_IN_ELEMENTS_ASSET_BLINDER 0x03 +#define WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX 0x04 +#define WALLY_PSBT_IN_ELEMENTS_TXOUT_PROOF 0x05 +#define WALLY_PSBT_IN_ELEMENTS_GENESIS_HASH 0x06 +#define WALLY_PSBT_IN_ELEMENTS_CLAIM_SCRIPT 0x07 +#endif /* BUILD ELEMENTS */ + #define WALLY_PSBT_OUT_REDEEM_SCRIPT 0x00 #define WALLY_PSBT_OUT_WITNESS_SCRIPT 0x01 #define WALLY_PSBT_OUT_BIP32_DERIVATION 0x02 +#ifdef BUILD_ELEMENTS +#define WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT 0x00 +#define WALLY_PSBT_OUT_ELEMENTS_VALUE_BLINDER 0x01 +#define WALLY_PSBT_OUT_ELEMENTS_ASSET_COMMITMENT 0x02 +#define WALLY_PSBT_OUT_ELEMENTS_ASSET_BLINDER 0x03 +#define WALLY_PSBT_OUT_ELEMENTS_RANGE_PROOF 0x04 +#define WALLY_PSBT_OUT_ELEMENTS_SURJECTION_PROOF 0x05 +#define WALLY_PSBT_OUT_ELEMENTS_BLINDING_PUBKEY 0x06 +#define WALLY_PSBT_OUT_ELEMENTS_NONCE_COMMITMENT 0x07 +#endif /* BUILD ELEMENTS */ + /* PSBT Version number */ #define WALLY_PSBT_HIGHEST_VERSION 0 diff --git a/src/psbt.c b/src/psbt.c index 0c179d1e4..c2fdd0b53 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -20,6 +20,8 @@ const uint8_t WALLY_PSBT_MAGIC[5] = {'p', 's', 'b', 't', 0xff}; #ifdef BUILD_ELEMENTS const uint8_t WALLY_ELEMENTS_PSBT_MAGIC[5] = {'p', 's', 'e', 't', 0xff}; +const uint8_t WALLY_ELEMENTS_ID[8] = {'e', 'l', 'e', 'm', 'e', 'n', 't', 's'}; +const size_t WALLY_ELEMENTS_ID_LEN = 8; #endif /* BUILD_ELEMENTS */ static bool pubkey_is_compressed(const unsigned char pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN]) { @@ -1524,6 +1526,37 @@ static int count_psbt_parts( case WALLY_PSBT_IN_BIP32_DERIVATION: input->num_keypaths++; break; + case WALLY_PSBT_PROPRIETARY_TYPE: { +#ifdef BUILD_ELEMENTS + uint64_t id_len; + bool valid_type = false; + + id_len = pull_varlength(&key, &key_len); + if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { + pull_skip(&key, &key_len, id_len); + + switch (pull_varint(&key, &key_len)) { + case WALLY_PSBT_IN_ELEMENTS_VALUE: + case WALLY_PSBT_IN_ELEMENTS_VALUE_BLINDER: + case WALLY_PSBT_IN_ELEMENTS_ASSET: + case WALLY_PSBT_IN_ELEMENTS_ASSET_BLINDER: + case WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX: + case WALLY_PSBT_IN_ELEMENTS_TXOUT_PROOF: + case WALLY_PSBT_IN_ELEMENTS_GENESIS_HASH: + case WALLY_PSBT_IN_ELEMENTS_CLAIM_SCRIPT: + valid_type = true; + break; + default: + valid_type = false; + } + } + if (valid_type) { + break; + } +#endif /* BUILD_ELEMENTS */ + } + /* For unknown case without elements or for unknown proprietary types */ + /* fall through */ /* Unknowns */ default: input->num_unknowns++; @@ -1554,6 +1587,37 @@ static int count_psbt_parts( case WALLY_PSBT_OUT_BIP32_DERIVATION: psbt_output->num_keypaths++; break; + case WALLY_PSBT_PROPRIETARY_TYPE: { +#ifdef BUILD_ELEMENTS + uint64_t id_len; + bool valid_type = false; + + id_len = pull_varlength(&key, &key_len); + if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { + pull_skip(&key, &key_len, id_len); + + switch (pull_varint(&key, &key_len)) { + case WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT: + case WALLY_PSBT_OUT_ELEMENTS_VALUE_BLINDER: + case WALLY_PSBT_OUT_ELEMENTS_ASSET_COMMITMENT: + case WALLY_PSBT_OUT_ELEMENTS_ASSET_BLINDER: + case WALLY_PSBT_OUT_ELEMENTS_RANGE_PROOF: + case WALLY_PSBT_OUT_ELEMENTS_SURJECTION_PROOF: + case WALLY_PSBT_OUT_ELEMENTS_BLINDING_PUBKEY: + case WALLY_PSBT_OUT_ELEMENTS_NONCE_COMMITMENT: + valid_type = true; + break; + default: + valid_type = false; + } + } + if (valid_type) { + break; + } +#endif /* BUILD_ELEMENTS */ + } + /* For unknown case without elements or for unknown proprietary types */ + /* fall through */ /* Unknowns */ default: psbt_output->num_unknowns++; @@ -1787,6 +1851,169 @@ static int pull_psbt_input( subfield_nomore_end(cursor, max, val, val_max); break; } + case WALLY_PSBT_PROPRIETARY_TYPE: { +#ifdef BUILD_ELEMENTS + uint64_t id_len; + bool valid_type = false; + + id_len = pull_varlength(&key, &key_len); + if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { + size_t subkey_len; + const unsigned char *subkey; + + /* Start parsing subkey */ + pull_subfield_start(&key, &key_len, id_len, &subkey, &subkey_len); + + switch (pull_varint(&subkey, &subkey_len)) { + case WALLY_PSBT_IN_ELEMENTS_VALUE: { + valid_type = true; + if (result->has_value) { + return WALLY_EINVAL; /* Already have value */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); + result->value = pull_le64(&val, &val_max); + subfield_nomore_end(cursor, max, val, val_max); + result->has_value = true; + break; + } + case WALLY_PSBT_IN_ELEMENTS_VALUE_BLINDER: { + valid_type = true; + if (result->value_blinder) { + return WALLY_EINVAL; /* Already have value blinding factor */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->value_blinder, + &result->value_blinder_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->value_blinder_len == 0) { + result->value_blinder = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_ASSET: { + valid_type = true; + if (result->asset) { + return WALLY_EINVAL; /* Already have asset */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->asset, + &result->asset_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->asset_len == 0) { + result->asset = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_ASSET_BLINDER: { + valid_type = true; + if (result->asset_blinder) { + return WALLY_EINVAL; /* Already have asset blinding factor */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->asset_blinder, + &result->asset_blinder_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->asset_blinder_len == 0) { + result->asset_blinder = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX: { + valid_type = true; + if (result->peg_in_tx) { + return WALLY_EINVAL; /* Already have asset */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_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->peg_in_tx); + if (ret != WALLY_OK) { + return ret; + } + pull_subfield_end(cursor, max, val, val_max); + break; + } + case WALLY_PSBT_IN_ELEMENTS_TXOUT_PROOF: { + valid_type = true; + if (result->txout_proof) { + return WALLY_EINVAL; /* Already have txout proof */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->txout_proof, + &result->txout_proof_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->txout_proof_len == 0) { + result->txout_proof = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_GENESIS_HASH: { + valid_type = true; + if (result->genesis_hash) { + return WALLY_EINVAL; /* Already have genesis hash */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->genesis_hash, + &result->genesis_hash_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->genesis_hash_len == 0) { + result->genesis_hash = wally_malloc(1); + } + break; + } + case WALLY_PSBT_IN_ELEMENTS_CLAIM_SCRIPT: { + valid_type = true; + if (result->claim_script) { + return WALLY_EINVAL; /* Already have asset */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->claim_script, + &result->claim_script_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->claim_script_len == 0) { + result->claim_script = wally_malloc(1); + } + break; + } + default: + valid_type = false; + } + } + if (valid_type) { + break; + } +#endif /* BUILD_ELEMENTS */ + } + /* For unknown case without elements or for unknown proprietary types */ + /* fall through */ /* Unknowns */ default: { ret = pull_unknown_key_value(cursor, max, pre_key, result->unknowns); @@ -1870,6 +2097,172 @@ static int pull_psbt_output( } break; } + case WALLY_PSBT_PROPRIETARY_TYPE: { +#ifdef BUILD_ELEMENTS + uint64_t id_len; + bool valid_type = false; + + id_len = pull_varlength(&key, &key_len); + if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { + size_t subkey_len; + const unsigned char *subkey; + + /* Start parsing subkey */ + pull_subfield_start(&key, &key_len, id_len, &subkey, &subkey_len); + + switch (pull_varint(&subkey, &subkey_len)) { + case WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT: { + valid_type = true; + if (result->value_commitment) { + return WALLY_EINVAL; /* Already have value commitment */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->value_commitment, + &result->value_commitment_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->value_commitment_len == 0) { + result->value_commitment = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_VALUE_BLINDER: { + valid_type = true; + if (result->value_blinder) { + return WALLY_EINVAL; /* Already have value blinder */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->value_blinder, + &result->value_blinder_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->value_blinder_len == 0) { + result->value_blinder = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_ASSET_COMMITMENT: { + valid_type = true; + if (result->asset_commitment) { + return WALLY_EINVAL; /* Already have asset commitment */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->asset_commitment, + &result->asset_commitment_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->asset_commitment_len == 0) { + result->asset_commitment = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_ASSET_BLINDER: { + valid_type = true; + if (result->asset_blinder) { + return WALLY_EINVAL; /* Already have asset blinder */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->asset_blinder, + &result->asset_blinder_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->asset_blinder_len == 0) { + result->asset_blinder = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_RANGE_PROOF: { + valid_type = true; + if (result->range_proof) { + return WALLY_EINVAL; /* Already have range proof */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->range_proof, + &result->range_proof_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->range_proof_len == 0) { + result->range_proof = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_SURJECTION_PROOF: { + valid_type = true; + if (result->surjection_proof) { + return WALLY_EINVAL; /* Already have surjection proof */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->surjection_proof, + &result->surjection_proof_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->surjection_proof_len == 0) { + result->surjection_proof = wally_malloc(1); + } + break; + } + case WALLY_PSBT_OUT_ELEMENTS_BLINDING_PUBKEY: { + const unsigned char *val; + size_t val_max; + valid_type = true; + if (result->has_blinding_pubkey) { + return WALLY_EINVAL; /* Already have blinding pubkey */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + /* Start parsing the value field. */ + pull_subfield_start(cursor, max, + pull_varint(cursor, max), + &val, &val_max); + if (val_max != EC_PUBLIC_KEY_UNCOMPRESSED_LEN && val_max != EC_PUBLIC_KEY_LEN) { + return WALLY_EINVAL; /* Size of key is unexpected */ + } + memcpy(result->blinding_pubkey, val, val_max); + result->has_blinding_pubkey = true; + + pull_subfield_end(cursor, max, val, val_max); + break; + } + case WALLY_PSBT_OUT_ELEMENTS_NONCE_COMMITMENT: { + valid_type = true; + if (result->nonce_commitment) { + return WALLY_EINVAL; /* Already have nonce commitment */ + } + subfield_nomore_end(&key, &key_len, subkey, subkey_len); + + if (!clone_varlength(&result->nonce_commitment, + &result->nonce_commitment_len, + cursor, max)) { + return WALLY_ENOMEM; + } + if (result->nonce_commitment_len == 0) { + result->nonce_commitment = wally_malloc(1); + } + break; + } + default: + valid_type = false; + } + } + if (valid_type) { + break; + } +#endif /* BUILD_ELEMENTS */ + } + /* For unknown case without elements or for unknown proprietary types */ + /* fall through */ /* Unknowns */ default: { ret = pull_unknown_key_value(cursor, max, pre_key, result->unknowns); @@ -2091,6 +2484,21 @@ static void push_psbt_key_with_pubkey( } } +#ifdef BUILD_ELEMENTS +/* Common case of pushing elements proprietary type keys */ +static void push_psbt_elements_key( + unsigned char **cursor, size_t *max, + uint64_t type, const void *extra, size_t extra_len) +{ + push_varint(cursor, max, varint_get_length(WALLY_PSBT_PROPRIETARY_TYPE) + + varint_get_length(WALLY_ELEMENTS_ID_LEN) + + WALLY_ELEMENTS_ID_LEN + varint_get_length(type) + extra_len); + push_varint(cursor, max, WALLY_PSBT_PROPRIETARY_TYPE); + push_varbuff(cursor, max, WALLY_ELEMENTS_ID, WALLY_ELEMENTS_ID_LEN); + push_bytes(cursor, max, extra, extra_len); +} +#endif /* BUILD_ELEMENTS */ + static int push_length_and_tx( unsigned char **cursor, size_t *max, const struct wally_tx *tx, uint32_t flags) @@ -2239,6 +2647,47 @@ static int push_psbt_input( push_varint(cursor, max, wit_len); push_witness_stack(cursor, max, input->final_witness); } +#ifdef BUILD_ELEMENTS + /* Confidential Assets blinding data */ + if (input->has_value) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_VALUE, NULL, 0); + push_le64(cursor, max, input->value); + } + if (input->value_blinder) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_VALUE_BLINDER, NULL, 0); + push_varbuff(cursor, max, input->value_blinder, input->value_blinder_len); + } + if (input->asset) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_ASSET, NULL, 0); + push_varbuff(cursor, max, input->asset, input->asset_len); + } + if (input->asset_blinder) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_ASSET_BLINDER, NULL, 0); + push_varbuff(cursor, max, input->asset_blinder, input->asset_blinder_len); + } + /* Peg ins */ + if (input->peg_in_tx) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX, NULL, 0); + ret = push_length_and_tx(cursor, max, + input->peg_in_tx, + WALLY_TX_FLAG_USE_WITNESS); + if (ret != WALLY_OK) { + return ret; + } + } + if (input->txout_proof) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_TXOUT_PROOF, NULL, 0); + push_varbuff(cursor, max, input->txout_proof, input->txout_proof_len); + } + if (input->genesis_hash) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_GENESIS_HASH, NULL, 0); + push_varbuff(cursor, max, input->genesis_hash, input->genesis_hash_len); + } + if (input->claim_script) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_CLAIM_SCRIPT, NULL, 0); + push_varbuff(cursor, max, input->claim_script, input->claim_script_len); + } +#endif /* BUILD_ELEMENTS */ /* Unknowns */ if (input->unknowns) { for (i = 0; i < input->unknowns->num_items; ++i) { @@ -2280,6 +2729,45 @@ static int push_psbt_output( &keypaths->items[i]); } } +#ifdef BUILD_ELEMENTS + if (output->value_commitment) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT, NULL, 0); + push_varbuff(cursor, max, output->value_commitment, output->value_commitment_len); + } + if (output->value_blinder) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_VALUE_BLINDER, NULL, 0); + push_varbuff(cursor, max, output->value_blinder, output->value_blinder_len); + } + if (output->asset_commitment) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_ASSET_COMMITMENT, NULL, 0); + push_varbuff(cursor, max, output->asset_commitment, output->asset_commitment_len); + } + if (output->asset_blinder) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_ASSET_BLINDER, NULL, 0); + push_varbuff(cursor, max, output->asset_blinder, output->asset_blinder_len); + } + if (output->range_proof) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_RANGE_PROOF, NULL, 0); + push_varbuff(cursor, max, output->range_proof, output->range_proof_len); + } + if (output->surjection_proof) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_SURJECTION_PROOF, NULL, 0); + push_varbuff(cursor, max, output->surjection_proof, output->surjection_proof_len); + } + if (output->has_blinding_pubkey) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_BLINDING_PUBKEY, NULL, 0); + if (pubkey_is_compressed(output->blinding_pubkey)) { + push_varbuff(cursor, max, output->blinding_pubkey, EC_PUBLIC_KEY_LEN); + } + else { + push_varbuff(cursor, max, output->blinding_pubkey, EC_PUBLIC_KEY_UNCOMPRESSED_LEN); + } + } + if (output->nonce_commitment) { + push_psbt_elements_key(cursor, max, WALLY_PSBT_OUT_ELEMENTS_NONCE_COMMITMENT, NULL, 0); + push_varbuff(cursor, max, output->nonce_commitment, output->nonce_commitment_len); + } +#endif /* BUILD_ELEMENTS */ /* Unknowns */ if (output->unknowns) { for (i = 0; i < output->unknowns->num_items; ++i) { From 732136cdf6e49bb424a29cacaed419ea820db79b Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 22 Jun 2020 20:58:22 -0500 Subject: [PATCH 23/37] pset: there are bools in the header file so it needs the bool header --- include/wally_psbt.h | 1 + src/psbt.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wally_psbt.h b/include/wally_psbt.h index 78d7ae936..30a13222d 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -3,6 +3,7 @@ #include "wally_transaction.h" #include "wally_core.h" +#include #ifdef __cplusplus extern "C" { diff --git a/src/psbt.c b/src/psbt.c index c2fdd0b53..e9ae78967 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -9,7 +9,6 @@ #include #include -#include #include #include "transaction_shared.h" #include "script_int.h" From 3943338b13c7d8644659566f811d0a7a2c4eb7e4 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 22 Jun 2020 21:06:04 -0500 Subject: [PATCH 24/37] pset: pass in correct flags for elements tx parsing libwally wants you to flag in what 'type' of transaction it's handling in a few places (in others it seems to be able to figure it out for itself) --- src/psbt.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index e9ae78967..39f4bce93 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -1436,6 +1436,7 @@ static void free_psbt_count(struct psbt_counts *counts) static int count_psbt_parts( const unsigned char *bytes, size_t bytes_len, + bool is_elements, struct psbt_counts **output) { int ret; @@ -1462,6 +1463,7 @@ static int count_psbt_parts( bool expect_wit; const unsigned char *val; size_t val_max; + int flags = 0; subfield_nomore_end(&bytes, &bytes_len, key, key_len); /* Value should be a tx */ @@ -1471,7 +1473,10 @@ static int count_psbt_parts( ret = WALLY_EINVAL; goto fail; } - ret = analyze_tx(val, val_max, 0, &result->num_inputs, &result->num_outputs, &expect_wit); + if (is_elements) { + flags |= WALLY_TX_FLAG_USE_ELEMENTS; + } + ret = analyze_tx(val, val_max, flags, &result->num_inputs, &result->num_outputs, &expect_wit); if (ret != WALLY_OK) { goto fail; } @@ -1646,6 +1651,7 @@ static int pull_psbt_input( const unsigned char **cursor, size_t *max, struct psbt_input_counts counts, + bool is_elements, struct wally_psbt_input *result) { int ret; @@ -1681,6 +1687,7 @@ static int pull_psbt_input( /* Process based on type */ switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_IN_NON_WITNESS_UTXO: { + int flags = 0; if (result->non_witness_utxo) { return WALLY_EINVAL; /* We already have a non witness utxo */ } @@ -1690,8 +1697,10 @@ static int pull_psbt_input( pull_subfield_start(cursor, max, pull_varint(cursor, max), &val, &val_max); - - ret = wally_tx_from_bytes(val, val_max, 0, + if (is_elements) { + flags |= WALLY_TX_FLAG_USE_ELEMENTS; + } + ret = wally_tx_from_bytes(val, val_max, flags, &result->non_witness_utxo); if (ret != WALLY_OK) { return ret; @@ -1943,7 +1952,7 @@ static int pull_psbt_input( pull_varint(cursor, max), &val, &val_max); - ret = wally_tx_from_bytes(val, val_max, 0, + ret = wally_tx_from_bytes(val, val_max, WALLY_TX_FLAG_USE_ELEMENTS, &result->peg_in_tx); if (ret != WALLY_OK) { return ret; @@ -2287,6 +2296,7 @@ int wally_psbt_from_bytes( size_t i, key_len; struct psbt_counts *counts = NULL; struct wally_psbt *result = NULL; + bool is_elements = false; TX_CHECK_OUTPUT; @@ -2301,6 +2311,7 @@ int wally_psbt_from_bytes( ret = WALLY_EINVAL; /* Invalid Magic */ goto fail; } + is_elements = true; #else ret = WALLY_EINVAL; /* Invalid Magic */ goto fail; @@ -2308,7 +2319,7 @@ int wally_psbt_from_bytes( } /* Get a count of the psbt parts */ - if (count_psbt_parts(bytes, bytes_len, &counts) != WALLY_OK) { + if (count_psbt_parts(bytes, bytes_len, is_elements, &counts) != WALLY_OK) { ret = WALLY_EINVAL; goto fail; } @@ -2335,6 +2346,7 @@ int wally_psbt_from_bytes( /* Process based on type */ switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_GLOBAL_UNSIGNED_TX: { + int flags = 0; if (result->tx) { ret = WALLY_EINVAL; /* We already have a global tx */ goto fail; @@ -2345,7 +2357,10 @@ int wally_psbt_from_bytes( 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 (is_elements) { + flags |= WALLY_TX_FLAG_USE_ELEMENTS; + } + ret = wally_tx_from_bytes(val, val_max, flags, &result->tx); if (ret != WALLY_OK) { goto fail; } @@ -2405,7 +2420,7 @@ int wally_psbt_from_bytes( /* Read inputs */ for (i = 0; i < counts->num_inputs; ++i) { - ret = pull_psbt_input(&bytes, &bytes_len, counts->input_counts[i], + ret = pull_psbt_input(&bytes, &bytes_len, counts->input_counts[i], is_elements, &result->inputs[i]); /* Increment this now, might be partially initialized! */ result->num_inputs++; From b3cd1e7fa3eb4f3378615d8e9c2714a01409d49c Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Jul 2020 21:53:18 -0500 Subject: [PATCH 25/37] .gitignore: ignore ctags --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index f99c9bc9d..79e654972 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,9 @@ docs/source/transaction.rst docs/source/elements.rst .idea/ +# ctags +tags + # MacOS **/.DS_Store From 5bf2bdc0e4e7d766433629d94a1afda0dcd62ee1 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Jul 2020 22:21:18 -0500 Subject: [PATCH 26/37] pset: use correct serialization for witness_utxo elementsd serializes the asset, value, nonce, and scriptpubkey for a 'witness_utxo' on the input; we were just serializing the amount + scriptpubkey, which made libwally psbt's incompatible with elementsd originated psets. --- src/psbt.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++- src/pullpush.c | 10 +++ src/pullpush.h | 1 + 3 files changed, 179 insertions(+), 2 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index 39f4bce93..c05df840c 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -4,6 +4,7 @@ #include "ccan/ccan/build_assert/build_assert.h" #include +#include #include #include #include @@ -1405,6 +1406,105 @@ static int pull_unknown_key_value(const unsigned char **cursor, return WALLY_OK; } +#ifdef BUILD_ELEMENTS +static size_t push_elements_bytes_size(const struct wally_tx_output *out) +{ + size_t size = 0; + size += out->asset_len == 0 ? 1 : out->asset_len; + size += out->value_len == 0 ? 1 : out->value_len; + size += out->nonce_len == 0 ? 1 : out->nonce_len; + size += out->script_len == 0 ? 1 : out->script_len + 1; + return size; +} +static void push_elements_bytes(unsigned char **cursor, + size_t *max, + unsigned char *value, + size_t val_len) +{ + if (value == NULL) { + unsigned char empty[1] = { 0x00 }; + push_bytes(cursor, max, empty, sizeof(empty)); + } else { + push_bytes(cursor, max, + value, val_len); + } +} + +static int pull_elements_confidential(const unsigned char **cursor, + size_t *max, + unsigned char **value, + size_t *val_len, + size_t prefixA, size_t prefixB, + size_t prefixedSize, size_t explicitSize) +{ + uint8_t type = peek_u8(cursor, max); + /*The first byte is always the 'version' + * which tells you what the value is */ + switch (type) { + /* Empty */ + case 0: + /* Pop off the type */ + pull_u8(cursor, max); + *value = NULL; + *val_len = 0; + return WALLY_OK; + /* Explicit size */ + case 1: + *value = wally_malloc(explicitSize); + pull_bytes(*value, explicitSize, cursor, max); + if (!*cursor) + return WALLY_EINVAL; + *val_len = explicitSize; + return WALLY_OK; + } + if (type == prefixA || type == prefixB) { + *value = wally_malloc(prefixedSize); + pull_bytes(*value, prefixedSize, cursor, max); + if (!*cursor) + return WALLY_EINVAL; + *val_len = prefixedSize; + return WALLY_OK; + } + return WALLY_EINVAL; +} + +/* Either returns a 33-byte commitment to a confidential value, or + * a 64-bit explicit value. */ +static int pull_confidential_value(const unsigned char **cursor, + size_t *max, + unsigned char **value, + size_t *val_len) + +{ + return pull_elements_confidential(cursor, max, value, val_len, + WALLY_TX_ASSET_CT_VALUE_PREFIX_A, WALLY_TX_ASSET_CT_VALUE_PREFIX_B, + WALLY_TX_ASSET_CT_VALUE_LEN, WALLY_TX_ASSET_CT_VALUE_UNBLIND_LEN); +} + +static int pull_confidential_asset(const unsigned char **cursor, + size_t *max, + unsigned char **asset, + size_t *asset_len) + +{ + return pull_elements_confidential(cursor, max, asset, asset_len, + WALLY_TX_ASSET_CT_ASSET_PREFIX_A, WALLY_TX_ASSET_CT_ASSET_PREFIX_B, + WALLY_TX_ASSET_CT_ASSET_LEN, WALLY_TX_ASSET_CT_ASSET_LEN); +} + +static int pull_nonce(const unsigned char **cursor, + size_t *max, + unsigned char **nonce, + size_t *nonce_len) + +{ + return pull_elements_confidential(cursor, max, nonce, nonce_len, + WALLY_TX_ASSET_CT_NONCE_PREFIX_A, WALLY_TX_ASSET_CT_NONCE_PREFIX_B, + WALLY_TX_ASSET_CT_NONCE_LEN, WALLY_TX_ASSET_CT_NONCE_LEN); +} + +#endif /* BUILD_ELEMENTS */ + struct psbt_input_counts { size_t num_unknowns; size_t num_keypaths; @@ -1715,11 +1815,42 @@ static int pull_psbt_input( return WALLY_EINVAL; /* We already have a witness utxo */ } subfield_nomore_end(cursor, max, key, key_len); - /* Start parsing the value field. */ pull_subfield_start(cursor, max, pull_varint(cursor, max), &val, &val_max); +#ifdef BUILD_ELEMENTS + if (is_elements) { + unsigned char *asset, *value, *nonce; + size_t asset_len, value_len, nonce_len; + if ((ret = pull_confidential_asset(&val, &val_max, &asset, &asset_len)) != WALLY_OK) { + return ret; + } + if ((ret = pull_confidential_value(&val, &val_max, &value, &value_len)) != WALLY_OK) { + return ret; + } + if ((ret = pull_nonce(&val, &val_max, &nonce, &nonce_len)) != WALLY_OK) { + return ret; + } + script_len = pull_varint(&val, &val_max); + script = pull_skip(&val, &val_max, script_len); + if (!script) { + return WALLY_EINVAL; + } + ret = wally_tx_elements_output_init_alloc(script, script_len, + asset, asset_len, + value, value_len, + nonce, nonce_len, + NULL, 0, NULL, 0, + &result->witness_utxo); + if (ret != WALLY_OK) { + return ret; + } + subfield_nomore_end(cursor, max, val, val_max); + break; + } +#endif /* BUILD_ELEMENTS */ + amount = pull_le64(&val, &val_max); script_len = pull_varint(&val, &val_max); script = pull_skip(&val, &val_max, script_len); @@ -2573,6 +2704,7 @@ static void push_keypath_item( static int push_psbt_input( unsigned char **cursor, size_t *max, + bool is_elements, const struct wally_psbt_input *input) { int ret; @@ -2590,11 +2722,41 @@ static int push_psbt_input( } /* Witness utxo */ +#ifdef BUILD_ELEMENTS + if (is_elements && input->witness_utxo) { + struct wally_tx_output *utxo = input->witness_utxo; + size_t buf_len = push_elements_bytes_size(utxo); + unsigned char buf[buf_len], *ptr = buf; + /* Push the asset, value, nonce, then scriptpubkey */ + push_psbt_key(cursor, max, WALLY_PSBT_IN_WITNESS_UTXO, NULL, 0); + + push_elements_bytes(&ptr, &buf_len, + utxo->asset, + utxo->asset_len); + push_elements_bytes(&ptr, &buf_len, + utxo->value, + utxo->value_len); + push_elements_bytes(&ptr, &buf_len, + utxo->nonce, + utxo->nonce_len); + push_varbuff(&ptr, &buf_len, + utxo->script, + utxo->script_len); + + assert(buf_len == 0); + push_varbuff(cursor, max, buf, sizeof(buf)); + } else +#else + if (is_elements) { + return WALLY_ERROR; + } +#endif /* BUILD_ELEMENTS */ if (input->witness_utxo) { unsigned char wit_bytes[50], *w = wit_bytes; /* Witness outputs can be no larger than 50 bytes as specified in BIP 141 */ size_t wit_max = sizeof(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, @@ -2833,9 +2995,13 @@ int wally_psbt_to_bytes( push_u8(&cursor, &max, WALLY_PSBT_SEPARATOR); /* Push each input and output */ + size_t is_elements; + if ((ret = wally_psbt_is_elements(psbt, &is_elements)) != WALLY_OK) { + return ret; + } for (i = 0; i < psbt->num_inputs; ++i) { struct wally_psbt_input *input = &psbt->inputs[i]; - ret = push_psbt_input(&cursor, &max, input); + ret = push_psbt_input(&cursor, &max, is_elements == 1, input); if (ret != WALLY_OK) { return ret; } diff --git a/src/pullpush.c b/src/pullpush.c index 0a1496c53..03eb9ad34 100644 --- a/src/pullpush.c +++ b/src/pullpush.c @@ -121,6 +121,16 @@ uint8_t pull_u8(const unsigned char **cursor, size_t *max) return v; } +uint8_t peek_u8(const unsigned char **cursor, size_t *max) +{ + uint8_t v = pull_u8(cursor, max); + if (*cursor) { + *cursor -= sizeof(v); + *max += sizeof(v); + } + return v; +} + void push_varint(unsigned char **cursor, size_t *max, uint64_t v) { unsigned char buf[sizeof(uint8_t) + sizeof(uint64_t)]; diff --git a/src/pullpush.h b/src/pullpush.h index 77d911f25..3922d487b 100644 --- a/src/pullpush.h +++ b/src/pullpush.h @@ -61,6 +61,7 @@ void push_le16(unsigned char **cursor, size_t *max, uint16_t v); uint16_t pull_le16(const unsigned char **cursor, size_t *max); void push_u8(unsigned char **cursor, size_t *max, uint8_t v); uint8_t pull_u8(const unsigned char **cursor, size_t *max); +uint8_t peek_u8(const unsigned char **cursor, size_t *max); void push_varint(unsigned char **cursor, size_t *max, uint64_t v); uint64_t pull_varint(const unsigned char **cursor, size_t *max); From d482f2ce4a47b395def05af6bfdd66c325b671f6 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Jul 2020 22:23:36 -0500 Subject: [PATCH 27/37] pset: fixup key parsing for prefix + subtype We were pulling out a subset of the key for the subtype, which wasn't correct. What we really want to do is finish using the already pulled 'key' object, which encompasses all of the subtype info (subtypes don't have length prefixes -- this might be a bug in elementsd?) --- src/psbt.c | 50 ++++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index c05df840c..a587398ca 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -1997,19 +1997,16 @@ static int pull_psbt_input( id_len = pull_varlength(&key, &key_len); if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { - size_t subkey_len; - const unsigned char *subkey; + /* Skip the elements_id prefix */ + pull_skip(&key, &key_len, WALLY_ELEMENTS_ID_LEN); - /* Start parsing subkey */ - pull_subfield_start(&key, &key_len, id_len, &subkey, &subkey_len); - - switch (pull_varint(&subkey, &subkey_len)) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_IN_ELEMENTS_VALUE: { valid_type = true; if (result->has_value) { return WALLY_EINVAL; /* Already have value */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); /* Start parsing the value field. */ pull_subfield_start(cursor, max, @@ -2025,7 +2022,7 @@ static int pull_psbt_input( if (result->value_blinder) { return WALLY_EINVAL; /* Already have value blinding factor */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->value_blinder, &result->value_blinder_len, @@ -2042,7 +2039,7 @@ static int pull_psbt_input( if (result->asset) { return WALLY_EINVAL; /* Already have asset */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->asset, &result->asset_len, @@ -2059,7 +2056,7 @@ static int pull_psbt_input( if (result->asset_blinder) { return WALLY_EINVAL; /* Already have asset blinding factor */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->asset_blinder, &result->asset_blinder_len, @@ -2076,7 +2073,7 @@ static int pull_psbt_input( if (result->peg_in_tx) { return WALLY_EINVAL; /* Already have asset */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); /* Start parsing the value field. */ pull_subfield_start(cursor, max, @@ -2096,7 +2093,7 @@ static int pull_psbt_input( if (result->txout_proof) { return WALLY_EINVAL; /* Already have txout proof */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->txout_proof, &result->txout_proof_len, @@ -2113,7 +2110,7 @@ static int pull_psbt_input( if (result->genesis_hash) { return WALLY_EINVAL; /* Already have genesis hash */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->genesis_hash, &result->genesis_hash_len, @@ -2130,7 +2127,7 @@ static int pull_psbt_input( if (result->claim_script) { return WALLY_EINVAL; /* Already have asset */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->claim_script, &result->claim_script_len, @@ -2243,19 +2240,16 @@ static int pull_psbt_output( id_len = pull_varlength(&key, &key_len); if (id_len == WALLY_ELEMENTS_ID_LEN && memcmp(key, WALLY_ELEMENTS_ID, id_len) == 0) { - size_t subkey_len; - const unsigned char *subkey; - - /* Start parsing subkey */ - pull_subfield_start(&key, &key_len, id_len, &subkey, &subkey_len); + /* Skip the elements_id prefix */ + pull_skip(&key, &key_len, WALLY_ELEMENTS_ID_LEN); - switch (pull_varint(&subkey, &subkey_len)) { + switch (pull_varint(&key, &key_len)) { case WALLY_PSBT_OUT_ELEMENTS_VALUE_COMMITMENT: { valid_type = true; if (result->value_commitment) { return WALLY_EINVAL; /* Already have value commitment */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->value_commitment, &result->value_commitment_len, @@ -2272,7 +2266,7 @@ static int pull_psbt_output( if (result->value_blinder) { return WALLY_EINVAL; /* Already have value blinder */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->value_blinder, &result->value_blinder_len, @@ -2289,7 +2283,7 @@ static int pull_psbt_output( if (result->asset_commitment) { return WALLY_EINVAL; /* Already have asset commitment */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->asset_commitment, &result->asset_commitment_len, @@ -2306,7 +2300,7 @@ static int pull_psbt_output( if (result->asset_blinder) { return WALLY_EINVAL; /* Already have asset blinder */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->asset_blinder, &result->asset_blinder_len, @@ -2323,7 +2317,7 @@ static int pull_psbt_output( if (result->range_proof) { return WALLY_EINVAL; /* Already have range proof */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->range_proof, &result->range_proof_len, @@ -2340,7 +2334,7 @@ static int pull_psbt_output( if (result->surjection_proof) { return WALLY_EINVAL; /* Already have surjection proof */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->surjection_proof, &result->surjection_proof_len, @@ -2359,7 +2353,7 @@ static int pull_psbt_output( if (result->has_blinding_pubkey) { return WALLY_EINVAL; /* Already have blinding pubkey */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); /* Start parsing the value field. */ pull_subfield_start(cursor, max, @@ -2379,7 +2373,7 @@ static int pull_psbt_output( if (result->nonce_commitment) { return WALLY_EINVAL; /* Already have nonce commitment */ } - subfield_nomore_end(&key, &key_len, subkey, subkey_len); + subfield_nomore_end(cursor, max, key, key_len); if (!clone_varlength(&result->nonce_commitment, &result->nonce_commitment_len, From 40883bfd7c2264848d091d4ed71eaab63a46ff60 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Jul 2020 22:25:24 -0500 Subject: [PATCH 28/37] pset: fixup spacing issue was breaking travis --- src/psbt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index a587398ca..40a3818fc 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -2630,8 +2630,8 @@ static void push_psbt_elements_key( uint64_t type, const void *extra, size_t extra_len) { push_varint(cursor, max, varint_get_length(WALLY_PSBT_PROPRIETARY_TYPE) - + varint_get_length(WALLY_ELEMENTS_ID_LEN) - + WALLY_ELEMENTS_ID_LEN + varint_get_length(type) + extra_len); + + varint_get_length(WALLY_ELEMENTS_ID_LEN) + + WALLY_ELEMENTS_ID_LEN + varint_get_length(type) + extra_len); push_varint(cursor, max, WALLY_PSBT_PROPRIETARY_TYPE); push_varbuff(cursor, max, WALLY_ELEMENTS_ID, WALLY_ELEMENTS_ID_LEN); push_bytes(cursor, max, extra, extra_len); From 501c4493a30e5bae3e828b2deaaff682127c7dc4 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Jul 2020 22:25:47 -0500 Subject: [PATCH 29/37] pset: fixup serialization of proprietary elements keys We were forgetting to add the subtype for proprietary element's keys --- src/psbt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psbt.c b/src/psbt.c index 40a3818fc..b937fb3b9 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -2634,6 +2634,7 @@ static void push_psbt_elements_key( + WALLY_ELEMENTS_ID_LEN + varint_get_length(type) + extra_len); push_varint(cursor, max, WALLY_PSBT_PROPRIETARY_TYPE); push_varbuff(cursor, max, WALLY_ELEMENTS_ID, WALLY_ELEMENTS_ID_LEN); + push_varint(cursor, max, type); push_bytes(cursor, max, extra, extra_len); } #endif /* BUILD_ELEMENTS */ From 796627d8557b79084a4025074b6b562958107f28 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Jul 2020 22:26:50 -0500 Subject: [PATCH 30/37] pset: include length of amount in value for WALLY_PSBT_IN_ELEMENTS_VALUEs Small oversight, omitted the length of the amount value when serializing --- src/psbt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/psbt.c b/src/psbt.c index b937fb3b9..06f2d84c9 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -2822,6 +2822,7 @@ static int push_psbt_input( /* Confidential Assets blinding data */ if (input->has_value) { push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_VALUE, NULL, 0); + push_varint(cursor, max, sizeof(leint64_t)); push_le64(cursor, max, input->value); } if (input->value_blinder) { From b1c5b54e99919f42e1778c404422044f6e525071 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Jul 2020 22:28:25 -0500 Subject: [PATCH 31/37] pset: add canonical pset example from elementsd An unsigned, parseable PSET that we now pass test against --- src/Makefile.am | 3 +++ src/test/data/pset.json | 6 ++++++ src/test/test_pset.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/test/data/pset.json create mode 100644 src/test/test_pset.py diff --git a/src/Makefile.am b/src/Makefile.am index ec91673b2..8a29db3f8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -286,6 +286,9 @@ if RUN_PYTHON_TESTS $(AM_V_at)$(PYTHON_TEST) test/test_transaction.py $(AM_V_at)$(PYTHON_TEST) test/test_wif.py $(AM_V_at)$(PYTHON_TEST) test/test_wordlist.py +if BUILD_ELEMENTS + $(AM_V_at)$(PYTHON_TEST) test/test_pset.py +endif if USE_SWIG_PYTHON $(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/address.py $(AM_V_at)$(PYTHON_SWIGTEST) swig_python/contrib/bip32.py diff --git a/src/test/data/pset.json b/src/test/data/pset.json new file mode 100644 index 000000000..4b49da887 --- /dev/null +++ b/src/test/data/pset.json @@ -0,0 +1,6 @@ +{ + "valid": [{ + "pset": "cHNldP8BAOYCAAAAAAEJoA5k8iAABF66mJyk85LBsCUTVVfErOwxmexTjCBEyQAAAAAA/f///wMBXOe5Y9N/jy1Ryvu6koqqniILi7xmBXFJnANiijhRuM4BAAAAAO5rFFgAF6kUpruHAKCJvLhrCf5VkrhUvQ4+bDKHAVznuWPTf48tUcr7upKKqp4iC4u8ZgVxSZwDYoo4UbjOAQAAAAA7msoAABepFII2rTwCO4NdVWyoBFVzHVM/c1IBhwFc57lj03+PLVHK+7qSiqqeIguLvGYFcUmcA2KKOFG4zgEAAAAAAAATqAAAAAAAAAABAUMBXOe5Y9N/jy1Ryvu6koqqniILi7xmBXFJnANiijhRuM4BAAAAASoF8gAAF6kUIUgXxch89mjFY+aFBdmAk9d8GcmHAQQWABTs8/AAycufhZ2FR4uYndwqd1C7ogv8CGVsZW1lbnRzAAgA8gUqAQAAAAv8CGVsZW1lbnRzAiBc57lj03+PLVHK+7qSiqqeIguLvGYFcUmcA2KKOFG4zgABABYAFMFIn/H6YCLLR2ukTXG9evUf5T5VC/wIZWxlbWVudHMGIQMYlbtO8ZBg4wdFP1vS+d59msC7iEjmr/VATyVestonAwABABYAFJpBlksv6mEJcXOPlyskNv1O3yn+C/wIZWxlbWVudHMGIQLkIFPXDMnWVGXBPhDwpi5zMF/WmosNkQLltvC445AA2QAA", + "len": 546 + }] +} diff --git a/src/test/test_pset.py b/src/test/test_pset.py new file mode 100644 index 000000000..4af6e851f --- /dev/null +++ b/src/test/test_pset.py @@ -0,0 +1,31 @@ +import json +import os +import unittest +from util import * + +class PSETTests(unittest.TestCase): + + def test_serialization(self): + """Testing serialization and deserialization""" + self.maxDiff = None + with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/pset.json')) as f: + d = json.load(f) + valids = d['valid'] + + for valid in valids: + psbt = pointer(wally_psbt()) + self.assertEqual(WALLY_OK, wally_psbt_from_base64(valid['pset'].encode('utf-8'), psbt)) + ret, reser = wally_psbt_to_base64(psbt) + self.assertEqual(WALLY_OK, ret) + self.assertEqual(valid['pset'], reser) + ret, length = wally_psbt_get_length(psbt) + self.assertEqual(WALLY_OK, ret) + self.assertEqual(length, valid['len']) + + +if __name__ == '__main__': + _, val = wally_is_elements_build() + if val != 0: + unittest.main() + else: + self.fail("Attempting to run Elements test without Elements enabled") From d4d9a08664cf1983984ebb1d835b4f43d1633279 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 9 Jul 2020 17:26:37 -0500 Subject: [PATCH 32/37] pset: assign the result to the pset, not the passed in probably should be swallowed into originating problem commit --- src/psbt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index 06f2d84c9..4c13026b4 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -699,7 +699,7 @@ int wally_psbt_elements_input_set_value_blinder( return WALLY_ENOMEM; } wally_free(input->value_blinder); - input->value_blinder = value_blinder; + input->value_blinder = result_value_blinder; input->value_blinder_len = value_blinder_len; return WALLY_OK; } @@ -715,7 +715,7 @@ int wally_psbt_elements_input_set_asset( return WALLY_ENOMEM; } wally_free(input->asset); - input->asset = asset; + input->asset = result_asset; input->asset_len = asset_len; return WALLY_OK; } @@ -731,7 +731,7 @@ int wally_psbt_elements_input_set_asset_blinder( return WALLY_ENOMEM; } wally_free(input->asset_blinder); - input->asset_blinder = asset_blinder; + input->asset_blinder = result_asset_blinder; input->asset_blinder_len = asset_blinder_len; return WALLY_OK; } @@ -747,7 +747,7 @@ int wally_psbt_elements_input_set_peg_in_tx( return ret; } wally_tx_free(input->peg_in_tx); - input->peg_in_tx = peg_in_tx; + input->peg_in_tx = result_peg_in_tx; return ret; } @@ -763,7 +763,7 @@ int wally_psbt_elements_input_set_txout_proof( return WALLY_ENOMEM; } wally_free(input->txout_proof); - input->txout_proof = txout_proof; + input->txout_proof = result_txout_proof; input->txout_proof_len = txout_proof_len; return WALLY_OK; } @@ -779,7 +779,7 @@ int wally_psbt_elements_input_set_genesis_hash( return WALLY_ENOMEM; } wally_free(input->genesis_hash); - input->genesis_hash = genesis_hash; + input->genesis_hash = result_genesis_hash; input->genesis_hash_len = genesis_hash_len; return WALLY_OK; } @@ -795,7 +795,7 @@ int wally_psbt_elements_input_set_claim_script( return WALLY_ENOMEM; } wally_free(input->claim_script); - input->claim_script = claim_script; + input->claim_script = result_claim_script; input->claim_script_len = claim_script_len; return WALLY_OK; } From beb04f3132184fca73df90b987ba8b7937c7c6c7 Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 10 Jul 2020 15:11:39 -0500 Subject: [PATCH 33/37] pset: quick hack to create elements tx hash for signing --- src/psbt.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/psbt.c b/src/psbt.c index 4c13026b4..47ee559ff 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -3422,11 +3422,17 @@ int wally_sign_psbt( unsigned char pubkey[EC_PUBLIC_KEY_LEN], uncomp_pubkey[EC_PUBLIC_KEY_UNCOMPRESSED_LEN], sig[EC_SIGNATURE_LEN], der_sig[EC_SIGNATURE_DER_MAX_LEN + 1]; size_t i, j, der_sig_len; int ret; + bool is_elements; if (!psbt || !psbt->tx || !key || key_len != EC_PRIVATE_KEY_LEN) { return WALLY_EINVAL; } + if ((ret = wally_psbt_is_elements(psbt, &i)) != WALLY_OK) { + return ret; + } + is_elements = i == 1; + /* Get the pubkey */ if ((ret = wally_ec_public_key_from_private_key(key, key_len, pubkey, EC_PUBLIC_KEY_LEN)) != WALLY_OK) { return ret; @@ -3567,7 +3573,15 @@ int wally_sign_psbt( continue; } - if ((ret = wally_tx_get_btc_signature_hash(psbt->tx, i, scriptcode, scriptcode_len, input->witness_utxo->satoshi, sighash_type, WALLY_TX_FLAG_USE_WITNESS, sighash, SHA256_LEN)) != WALLY_OK) { + if (is_elements) { +#ifdef BUILD_ELEMENTS + if ((ret = wally_tx_get_elements_signature_hash(psbt->tx, i, scriptcode, scriptcode_len, input->witness_utxo->value, input->witness_utxo->value_len, sighash_type, WALLY_TX_FLAG_USE_WITNESS, sighash, SHA256_LEN)) != WALLY_OK) { + return ret; + } +#else + return WALLY_ERROR; +#endif /* BUILD_ELEMENTS */ + } else if ((ret = wally_tx_get_btc_signature_hash(psbt->tx, i, scriptcode, scriptcode_len, input->witness_utxo->satoshi, sighash_type, WALLY_TX_FLAG_USE_WITNESS, sighash, SHA256_LEN)) != WALLY_OK) { return ret; } } From 3935be89464cc370b5c80dbe383dbc23fca653a6 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 2 Jul 2020 13:50:31 -0500 Subject: [PATCH 34/37] configure: wouldn't compile MAP_ANON on Ubuntu 20.04 LTS Needs the _DEFAULT_SOURCE flag? Recently upgraded to Ubuntu20.04 which can't find MAP_ANON. I traced the problem to __USE_MISC not being flagged on, for who knows why. Some digging led me to this solution. Some further patches from Joseph complete the deprecation of _BSD_SOURCE and _SVID_SOURCE for the upcoming glibc 2.20 release. The first of these patches converts _BSD_SOURCE and _SVID_SOURCE into synonyms for _DEFAULT_SOURCE. The deprecated macros remain effective, but cause compile-time warnings to be generated if they are used (unless _DEFAULT_SOURCE is also defined). The second patch (mechanically) converts all of the uses of the __USE_SVID and __USE_BSD internal macros in glibc header files into __USE_MISC. Source: https://lwn.net/Articles/590381/ --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index 8fb802f5c..40cca2092 100644 --- a/configure.ac +++ b/configure.ac @@ -174,6 +174,9 @@ AX_CHECK_COMPILE_FLAG([-Wpedantic], [AM_CFLAGS="-Wpedantic $AM_CFLAGS"]) AX_CHECK_COMPILE_FLAG([-Wextra], [AM_CFLAGS="-Wextra $AM_CFLAGS"]) AX_CHECK_COMPILE_FLAG([-Wall], [AM_CFLAGS="-Wall $AM_CFLAGS"]) +# Needed for Ubuntu 20.04LTS when compiling as a library +AX_CHECK_COMPILE_FLAG([-D_DEFAULT_SOURCE=1], [AM_CFLAGS="-D_DEFAULT_SOURCE=1 $AM_CFLAGS"]) + # Disable some unhelpful errors from those enabled above AX_CHECK_COMPILE_FLAG([-Wno-unused-function], [AM_CFLAGS="$AM_CFLAGS -Wno-unused-function"]) AX_CHECK_COMPILE_FLAG([-Wno-long-long], [AM_CFLAGS="$AM_CFLAGS -Wno-long-long"]) From 7653b60385cfa156ab4c6ac1ab37a9add10e2bd7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Jul 2020 09:25:11 +0930 Subject: [PATCH 35/37] psbt: allow partial transactions. During PSBT preparation, it can reasonably contain unfinished transactions. In particular, we didn't allow marshalling of zero-output transactions. Signed-off-by: Rusty Russell Edited-By: niftynei --- src/psbt.c | 11 ++++++----- src/transaction.c | 32 +++++++++++++++++++++++--------- src/transaction_shared.h | 4 ++++ 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index 47ee559ff..4b379a624 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -2641,7 +2641,8 @@ static void push_psbt_elements_key( static int push_length_and_tx( unsigned char **cursor, size_t *max, - const struct wally_tx *tx, uint32_t flags) + const struct wally_tx *tx, uint32_t flags, + bool partial_ok) { int ret; size_t txlen; @@ -2661,7 +2662,7 @@ static int push_length_and_tx( return WALLY_OK; } - return wally_tx_to_bytes(tx, flags, p, txlen, &txlen); + return wally_partial_tx_to_bytes(tx, flags, p, txlen, partial_ok, &txlen); } static void push_witness_stack( @@ -2710,7 +2711,7 @@ static int push_psbt_input( 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); + WALLY_TX_FLAG_USE_WITNESS, false); if (ret != WALLY_OK) { return ret; } @@ -2842,7 +2843,7 @@ static int push_psbt_input( push_psbt_elements_key(cursor, max, WALLY_PSBT_IN_ELEMENTS_PEG_IN_TX, NULL, 0); ret = push_length_and_tx(cursor, max, input->peg_in_tx, - WALLY_TX_FLAG_USE_WITNESS); + WALLY_TX_FLAG_USE_WITNESS, false); if (ret != WALLY_OK) { return ret; } @@ -2969,7 +2970,7 @@ int wally_psbt_to_bytes( /* Global tx */ push_psbt_key(&cursor, &max, WALLY_PSBT_GLOBAL_UNSIGNED_TX, NULL, 0); - push_length_and_tx(&cursor, &max, psbt->tx, 0); + push_length_and_tx(&cursor, &max, psbt->tx, 0, true); /* version */ if (psbt->version > 0) { diff --git a/src/transaction.c b/src/transaction.c index e87ec6d3c..5355fa348 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -1972,7 +1972,8 @@ static int tx_to_bytes(const struct wally_tx *tx, uint32_t flags, unsigned char *bytes_out, size_t len, size_t *written, - bool is_elements) + bool is_elements, + bool partial_ok) { size_t n, i, j, witness_count; const bool anyonecanpay = opts && opts->sighash & WALLY_SIGHASH_ANYONECANPAY; @@ -1983,11 +1984,14 @@ static int tx_to_bytes(const struct wally_tx *tx, if (written) *written = 0; - if (!is_valid_tx(tx) || !tx->num_inputs || !tx->num_outputs || + if (!is_valid_tx(tx) || (flags & ~WALLY_TX_FLAG_USE_WITNESS) || !bytes_out || !written || tx_get_length(tx, opts, flags, &n, is_elements) != WALLY_OK) return WALLY_EINVAL; + if (!partial_ok && (!tx->num_inputs || !tx->num_outputs)) + return WALLY_EINVAL; + if (opts && (flags & WALLY_TX_FLAG_USE_WITNESS)) return WALLY_ERROR; /* Segwit tx hashing is handled elsewhere */ @@ -2138,9 +2142,10 @@ static int tx_to_bytes(const struct wally_tx *tx, return WALLY_OK; } -int wally_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, - unsigned char *bytes_out, size_t len, - size_t *written) +int wally_partial_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, + unsigned char *bytes_out, size_t len, + bool partial_ok, + size_t *written) { size_t is_elements = 0; @@ -2148,7 +2153,16 @@ int wally_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, if (wally_tx_is_elements(tx, &is_elements) != WALLY_OK) return WALLY_EINVAL; #endif - return tx_to_bytes(tx, NULL, flags, bytes_out, len, written, is_elements); + return tx_to_bytes(tx, NULL, flags, bytes_out, len, written, is_elements, + partial_ok); +} + + +int wally_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, + unsigned char *bytes_out, size_t len, + size_t *written) +{ + return wally_partial_tx_to_bytes(tx, flags, bytes_out, len, false, written); } static int tx_to_hex(const struct wally_tx *tx, uint32_t flags, @@ -2161,12 +2175,12 @@ static int tx_to_hex(const struct wally_tx *tx, uint32_t flags, if (!output) return WALLY_EINVAL; - ret = tx_to_bytes(tx, NULL, flags, buff_p, sizeof(buff), &n, is_elements); + ret = tx_to_bytes(tx, NULL, flags, buff_p, sizeof(buff), &n, is_elements, false); if (ret == WALLY_OK) { if (n > sizeof(buff)) { if ((buff_p = wally_malloc(n)) == NULL) return WALLY_ENOMEM; - ret = tx_to_bytes(tx, NULL, flags, buff_p, n, &written, is_elements); + ret = tx_to_bytes(tx, NULL, flags, buff_p, n, &written, is_elements, false); if (n != written) ret = WALLY_ERROR; /* Length calculated incorrectly */ } @@ -2669,7 +2683,7 @@ static int tx_get_signature_hash(const struct wally_tx *tx, goto fail; } - if ((ret = tx_to_bytes(tx, &opts, 0, buff_p, n, &n2, is_elements != 0)) != WALLY_OK) + if ((ret = tx_to_bytes(tx, &opts, 0, buff_p, n, &n2, is_elements != 0, false)) != WALLY_OK) goto fail; if (n != n2) diff --git a/src/transaction_shared.h b/src/transaction_shared.h index 109b7c0f1..77a2eca1e 100644 --- a/src/transaction_shared.h +++ b/src/transaction_shared.h @@ -20,6 +20,10 @@ int analyze_tx(const unsigned char *bytes, size_t bytes_len, struct wally_tx_witness_stack *clone_witness( const struct wally_tx_witness_stack *stack); int clone_tx(struct wally_tx *tx, struct wally_tx **output); +int wally_partial_tx_to_bytes(const struct wally_tx *tx, uint32_t flags, + unsigned char *bytes_out, size_t len, + bool partial_ok, + size_t *written); #ifdef __cplusplus } From 55e9f6a11e28bfe9cf4aa9dd6c8668a829871344 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 8 Jul 2020 15:15:26 -0500 Subject: [PATCH 36/37] expose wally_tx_output_clone @niftynei needs it for the clightning things --- include/wally_transaction.h | 4 ++++ src/transaction.c | 15 ++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/wally_transaction.h b/include/wally_transaction.h index 602725867..d7daf6497 100644 --- a/include/wally_transaction.h +++ b/include/wally_transaction.h @@ -396,6 +396,9 @@ WALLY_CORE_API int wally_tx_get_witness_count( WALLY_CORE_API int wally_tx_free(struct wally_tx *tx); #endif /* SWIG_PYTHON */ +WALLY_CORE_API int wally_clone_output_to(struct wally_tx_output *dst, + const struct wally_tx_output *src); + /** * Return the length of transaction once serialized into bytes. * @@ -946,6 +949,7 @@ WALLY_CORE_API int wally_tx_elements_issuance_calculate_reissuance_token( unsigned char *bytes_out, size_t len); + #endif /* BUILD_ELEMENTS */ #ifdef __cplusplus diff --git a/src/transaction.c b/src/transaction.c index 5355fa348..7f47917c5 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -775,8 +775,8 @@ int wally_tx_input_free(struct wally_tx_input *input) return tx_input_free(input, true); } -static bool clone_output_to(struct wally_tx_output *dst, - const struct wally_tx_output *src) +int wally_clone_output_to(struct wally_tx_output *dst, + const struct wally_tx_output *src) { unsigned char *new_script = NULL; #ifdef BUILD_ELEMENTS @@ -784,7 +784,7 @@ static bool clone_output_to(struct wally_tx_output *dst, *new_surjectionproof = NULL, *new_rangeproof = NULL; #endif if (!dst || !src) - return false; + return WALLY_EINVAL; #ifdef BUILD_ELEMENTS if (!clone_bytes(&new_asset, src->asset, src->asset_len) || @@ -804,7 +804,7 @@ static bool clone_output_to(struct wally_tx_output *dst, clear_and_free(new_surjectionproof, src->surjectionproof_len); clear_and_free(new_rangeproof, src->rangeproof_len); #endif - return false; + return WALLY_ENOMEM; } memcpy(dst, src, sizeof(*src)); @@ -816,7 +816,7 @@ static bool clone_output_to(struct wally_tx_output *dst, dst->surjectionproof = new_surjectionproof; dst->rangeproof = new_rangeproof; #endif - return true; + return WALLY_OK; } static int tx_elements_output_proof_init( @@ -1358,6 +1358,7 @@ int wally_tx_remove_input(struct wally_tx *tx, size_t index) int wally_tx_add_output(struct wally_tx *tx, const struct wally_tx_output *output) { uint64_t total; + int ret; const bool is_elements = output->features & WALLY_TX_IS_ELEMENTS; if (!is_elements) { if (!is_valid_tx(tx) || !is_valid_tx_output(output) || @@ -1379,8 +1380,8 @@ int wally_tx_add_output(struct wally_tx *tx, const struct wally_tx_output *outpu tx->outputs = p; tx->outputs_allocation_len += 1; } - if (!clone_output_to(tx->outputs + tx->num_outputs, output)) - return WALLY_ENOMEM; + if ((ret = wally_clone_output_to(tx->outputs + tx->num_outputs, output)) != WALLY_OK) + return ret; tx->num_outputs += 1; return WALLY_OK; From 1f45aef1e990e945710691e0e3637312f9a84e73 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 8 Jul 2020 15:31:08 -0500 Subject: [PATCH 37/37] psbt: clone passed in tx_output, makes us pset compatible --- src/psbt.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/psbt.c b/src/psbt.c index 4b379a624..db3bbbe32 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -555,15 +555,21 @@ int wally_psbt_input_set_witness_utxo( struct wally_psbt_input *input, struct wally_tx_output *witness_utxo) { - int ret = WALLY_OK; - struct wally_tx_output *result_witness_utxo; + int res = WALLY_OK; + struct wally_tx_output *output; - if ((ret = wally_tx_output_init_alloc(witness_utxo->satoshi, witness_utxo->script, witness_utxo->script_len, &result_witness_utxo)) != WALLY_OK) { - return ret; + output = wally_malloc(sizeof(struct wally_tx_output)); + if (!output) return WALLY_ENOMEM; + wally_clear((void *)output, sizeof(struct wally_tx_output)); + + /* Since Elements has a different setup, + * we just clone the passed in output */ + if ((res = wally_clone_output_to(output, witness_utxo)) != WALLY_OK) { + return res; } wally_tx_output_free(input->witness_utxo); - input->witness_utxo = result_witness_utxo; - return ret; + input->witness_utxo = output; + return res; } int wally_psbt_input_set_redeem_script(