From e0c69cce4f33a14a78081367e506a7961a9bd221 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:39:04 +0930 Subject: [PATCH 01/16] 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 dcbc8e17b4be1a4465ce42f4e5d33c77adb2566c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:41:09 +0930 Subject: [PATCH 02/16] 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 ecf297f349fb701105e3c2f46192d671b9cf5733 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:42:09 +0930 Subject: [PATCH 03/16] 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 bc9448758..b3ec58971 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 0cb51e49932c77d519eca13fed19256470fe7f2a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:43:09 +0930 Subject: [PATCH 04/16] 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 b3ec58971..edeb515bf 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 e46b41f0cfc775ab6a242586a37f7eada03d4d9b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:27 +0930 Subject: [PATCH 05/16] 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 f96a9464d9655431e1a5c5559ef90148d439baf2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:55 +0930 Subject: [PATCH 06/16] 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 edeb515bf..8103fc947 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 52b43459de9e3a2f3aa4754b49c2ddae00a82f46 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:55 +0930 Subject: [PATCH 07/16] 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 8103fc947..712da4548 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 986db7e9dfbf199056f93891b018fd0baf4f5f9a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 08/16] 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 712da4548..b509469a9 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 29a741f1aaa5dac04bd7d1fbcfa16cacfbbb7ef3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 09/16] 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 b509469a9..445705ba7 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 562e33558c9b915c156ebc78128b127cf985a27b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 10/16] 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 445705ba7..e439b17e6 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 0874a862464c69dd4e6be84c766a7be42d02fcf4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 11/16] 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 e439b17e6..ae84fe20c 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 9549acbff0072b55c213391cf5e67258eb91cf64 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 12/16] 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 ae84fe20c..29db9e704 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 b3af98a2a9cb7f42aaf157f32378f8f4ac112b03 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 13/16] 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 29db9e704..51f95fde2 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 cf033cdc8245b1c8734af841b977260e972d9c71 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:45:56 +0930 Subject: [PATCH 14/16] 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 8ea5c4cd0eded91457a42cb24da5e4c7d7508575 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:46:42 +0930 Subject: [PATCH 15/16] 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 1cccf0581cba859d5da36eadca7ffed2eb2cf070 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 29 May 2020 10:47:12 +0930 Subject: [PATCH 16/16] 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); }