diff --git a/include/wally.hpp b/include/wally.hpp index 0e845b137..036e64d24 100644 --- a/include/wally.hpp +++ b/include/wally.hpp @@ -1641,6 +1641,17 @@ inline int psbt_sign_input_bip32(const PSBT& psbt, size_t index, size_t subindex return detail::check_ret(__FUNCTION__, ret); } +inline int psbt_signing_cache_disable(struct wally_psbt* psbt) { + int ret = ::wally_psbt_signing_cache_disable(psbt); + return detail::check_ret(__FUNCTION__, ret); +} + +template +inline int psbt_signing_cache_enable(const PSBT& psbt, uint32_t flags) { + int ret = ::wally_psbt_signing_cache_enable(detail::get_p(psbt), flags); + return detail::check_ret(__FUNCTION__, ret); +} + template inline int psbt_to_base64(const PSBT& psbt, uint32_t flags, char** output) { int ret = ::wally_psbt_to_base64(detail::get_p(psbt), flags, output); @@ -1897,6 +1908,12 @@ inline int tx_get_hash_prevouts(const TX& tx, size_t index, size_t num_inputs, B return detail::check_ret(__FUNCTION__, ret); } +template +inline int tx_get_input_signature_hash(const TX& tx, size_t index, const SCRIPTS& scripts, const ASSETS& assets, const VALUES& values, const SCRIPT& script, uint32_t key_version, uint32_t codesep_position, const ANNEX& annex, const GENESIS_BLOCKHASH& genesis_blockhash, uint32_t sighash, uint32_t flags, const CACHE& cache, BYTES_OUT& bytes_out) { + int ret = ::wally_tx_get_input_signature_hash(detail::get_p(tx), index, detail::get_p(scripts), detail::get_p(assets), detail::get_p(values), script.data(), script.size(), key_version, codesep_position, annex.data(), annex.size(), genesis_blockhash.data(), genesis_blockhash.size(), sighash, flags, detail::get_p(cache), bytes_out.data(), bytes_out.size()); + return detail::check_ret(__FUNCTION__, ret); +} + template inline int tx_get_length(const TX& tx, uint32_t flags, size_t* written) { int ret = ::wally_tx_get_length(detail::get_p(tx), flags, written); diff --git a/include/wally_psbt.h b/include/wally_psbt.h index 02ed4fd00..2e9da1832 100644 --- a/include/wally_psbt.h +++ b/include/wally_psbt.h @@ -141,6 +141,7 @@ struct wally_psbt { uint32_t pset_modifiable_flags; unsigned char genesis_blockhash[SHA256_LEN]; /* All zeros if not present */ #endif /* WALLY_ABI_NO_ELEMENTS */ + struct wally_map *signing_cache; }; #endif /* SWIG */ @@ -2592,6 +2593,36 @@ WALLY_CORE_API int wally_psbt_blind_alloc( struct wally_map **output); #endif /* WALLY_ABI_NO_ELEMENTS */ +/** + * Enable caching of intermediate data when signing a PSBT. + * + * This function should be called just before signing a PSBT or the first + * input being signed, or before computing a signature hash for the PSBT. + * If the PSBT is modified in a way that would affect the signatures produced, + * this function should be called again to ensure that old cached data is + * purged before signing again. + * + * :param psbt: PSBT to enable the signing cache for. Directly modifies this PSBT. + * :param flags: Flags controlling the signing cache. Must be 0. + * + * .. note:: The signing cache is local to the given PSBT and is not + *| serialized with it. + */ +WALLY_CORE_API int wally_psbt_signing_cache_enable( + struct wally_psbt *psbt, + uint32_t flags); + +/** + * Disable caching of intermediate data when signing a PSBT. + * + * This function can be called at any time to ensure that the PSBT signing + * cache data is not reused when signing again. + * + * :param psbt: PSBT to disable the signing cache for. Directly modifies this PSBT. + */ +WALLY_CORE_API int wally_psbt_signing_cache_disable( + struct wally_psbt *psbt); + /** * Sign PSBT inputs corresponding to a given private key. * @@ -2614,7 +2645,9 @@ WALLY_CORE_API int wally_psbt_sign( * * :param psbt: PSBT to sign. Directly modifies this PSBT. * :param hdkey: The parent extended key to derive signing keys from. - * :param flags: Flags controlling signing. Must be 0 or EC_FLAG_GRIND_R. + * :param flags: Flags controlling signing. Must be 0 or `EC_FLAG_GRIND_R`. + *| Note that unlike `wally_psbt_sign_input_bip32`, `EC_FLAG_ELEMENTS` + *| is determined automatically and should not included in ``flags``. * * .. note:: See https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#simple-signer-algorithm *| for a description of the signing algorithm. @@ -2633,7 +2666,8 @@ WALLY_CORE_API int wally_psbt_sign_bip32( * :param txhash: The signature hash to sign, from `wally_psbt_get_input_signature_hash`. * :param txhash_len: Size of ``txhash`` in bytes. Must be `WALLY_TXHASH_LEN`. * :param hdkey: The derived extended key to sign with. - * :param flags: Flags controlling signing. Must be 0 or EC_FLAG_GRIND_R. + * :param flags: Flags controlling signing. Must be 0 or `EC_FLAG_GRIND_R`, + *| logical or-d with `EC_FLAG_ELEMENTS` if ``psbt`` is a PSET. */ WALLY_CORE_API int wally_psbt_sign_input_bip32( struct wally_psbt *psbt, diff --git a/include/wally_transaction.h b/include/wally_transaction.h index 4b45b77dd..ae238db84 100644 --- a/include/wally_transaction.h +++ b/include/wally_transaction.h @@ -51,6 +51,12 @@ extern "C" { #define WALLY_SIGHASH_MASK 0x1f /* Mask for determining ALL/NONE/SINGLE */ #define WALLY_SIGHASH_TR_IN_MASK 0xc0 /* Taproot mask for determining input hash type */ +/*** tx-sighash-type Transaction signature hash flags */ +#define WALLY_SIGTYPE_PRE_SW 0x1 /* Pre-segwit signature hash */ +#define WALLY_SIGTYPE_SW_V0 0x2 /* Segwit v0 signature hash */ +#define WALLY_SIGTYPE_SW_V1 0x3 /* Segwit v1 (taproot) signature hash */ +#define WALLY_SIGTYPE_MASK 0xf /* Mask for signature hash in signature hash flags */ + #define WALLY_TX_ASSET_CT_EMPTY_PREFIX 0x00 #define WALLY_TX_ASSET_CT_EXPLICIT_PREFIX 0x01 #define WALLY_TX_ASSET_CT_VALUE_PREFIX_A 0x08 @@ -862,6 +868,63 @@ WALLY_CORE_API int wally_tx_get_signature_hash( unsigned char *bytes_out, size_t len); +/** + * Get the hash of the preimage for signing a transaction input. + * + * :param tx: The transaction to generate the signature hash from. + * :param index: The input index of the input being signed for. + * :param scripts: The scriptpubkeys of each input in the transaction, indexed + *| by their 0-based input index. For non-taproot signing, only the + *| scriptpubkey of ``index`` is required. + * :param assets: The asset commitments of each input in the transaction, + *| or NULL for non-Elements transactions. Ignored for non-taproot signing. + * :param values: The satoshi values(BTC) or value commitments(Elements) of + *| each input in the transaction. BTC values must be stored as bytes with + *| uint64/host endiannes. For non-taproot signing, only the value + *| of ``index`` is required. + * :param script: For segwit v0 signing, the scriptcode of the input to sign + *| for. For taproot, the leaf script to sign with if any. Ignored for + *| pre-segwit signing. + * :param script_len: Length of ``script`` in bytes. + * :param key_version: Version of pubkey in tapscript. Must be set + *| to `0x00` or `0x01` for taproot script-path signing. + * :param codesep_position: BIP342 codeseparator position + *| or ``WALLY_NO_CODESEPARATOR`` if none. Only used for taproot signing. + * :param annex: BIP341 annex, or NULL if none. + * :param annex_len: Length of ``annex`` in bytes. Only used for taproot signing. + * :param genesis_blockhash: The genesis blockhash of the chain to sign for, + *| or NULL for non-Elements transactions. Only used for taproot signing. + * :param genesis_blockhash_len: Length of ``genesis_blockhash`` in bytes. Must + *| be `SHA256_LEN` or 0. + * :param sighash: ``WALLY_SIGHASH_`` flags specifying the sighash flags + *| to sign with. + * :param flags: :ref:`tx-sighash-type` controlling signature hash generation. + * :param cache: An opaque cache for faster generation, or NULL to disable + *| caching. Must be empty on the first call to this function for a given + *| transaction, and only used for signing the inputs of the same ``tx``. + * :param bytes_out: Destination for the resulting signature hash. + * FIXED_SIZED_OUTPUT(len, bytes_out, SHA256_LEN) + */ +WALLY_CORE_API int wally_tx_get_input_signature_hash( + const struct wally_tx *tx, + size_t index, + const struct wally_map *scripts, + const struct wally_map *assets, + const struct wally_map *values, + const unsigned char *script, + size_t script_len, + uint32_t key_version, + uint32_t codesep_position, + const unsigned char *annex, + size_t annex_len, + const unsigned char *genesis_blockhash, + size_t genesis_blockhash_len, + uint32_t sighash, + uint32_t flags, + struct wally_map *cache, + unsigned char *bytes_out, + size_t len); + /** * Determine if a transaction is a coinbase transaction. * diff --git a/src/Makefile.am b/src/Makefile.am index 5c0084353..22c545a9c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -167,6 +167,7 @@ libwallycore_la_SOURCES = \ sign.c \ symmetric.c \ transaction.c \ + tx_io.c \ wif.c \ wordlist.c \ ccan/ccan/base64/base64.c \ diff --git a/src/amalgamation/combined.c b/src/amalgamation/combined.c index 206544232..3f1f7659d 100644 --- a/src/amalgamation/combined.c +++ b/src/amalgamation/combined.c @@ -75,6 +75,7 @@ #include "src/sign.c" #include "src/symmetric.c" #include "src/transaction.c" +#include "src/tx_io.c" #include "src/wif.c" #include "src/wordlist.c" diff --git a/src/data/bip341_vectors.json b/src/data/bip341_vectors.json index 11261b00b..a58ccb209 100644 --- a/src/data/bip341_vectors.json +++ b/src/data/bip341_vectors.json @@ -447,6 +447,50 @@ "auxiliary": { "fullySignedTx": "020000000001097de20cbff686da83a54981d2b9bab3586f4ca7e48f57f5b55963115f3b334e9c010000000000000000d7b7cab57b1393ace2d064f4d4a2cb8af6def61273e127517d44759b6dafdd990000000000fffffffff8e1f583384333689228c5d28eac13366be082dc57441760d957275419a41842000000006b4830450221008f3b8f8f0537c420654d2283673a761b7ee2ea3c130753103e08ce79201cf32a022079e7ab904a1980ef1c5890b648c8783f4d10103dd62f740d13daa79e298d50c201210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798fffffffff0689180aa63b30cb162a73c6d2a38b7eeda2a83ece74310fda0843ad604853b0100000000feffffffaa5202bdf6d8ccd2ee0f0202afbbb7461d9264a25e5bfd3c5a52ee1239e0ba6c0000000000feffffff956149bdc66faa968eb2be2d2faa29718acbfe3941215893a2a3446d32acd050000000000000000000e664b9773b88c09c32cb70a2a3e4da0ced63b7ba3b22f848531bbb1d5d5f4c94010000000000000000e9aa6b8e6c9de67619e6a3924ae25696bb7b694bb677a632a74ef7eadfd4eabf0000000000ffffffffa778eb6a263dc090464cd125c466b5a99667720b1c110468831d058aa1b82af10100000000ffffffff0200ca9a3b000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac807840cb0000000020ac9a87f5594be208f8532db38cff670c450ed2fea8fcdefcc9a663f78bab962b0141ed7c1647cb97379e76892be0cacff57ec4a7102aa24296ca39af7541246d8ff14d38958d4cc1e2e478e4d4a764bbfd835b16d4e314b72937b29833060b87276c030141052aedffc554b41f52b521071793a6b88d6dbca9dba94cf34c83696de0c1ec35ca9c5ed4ab28059bd606a4f3a657eec0bb96661d42921b5f50a95ad33675b54f83000141ff45f742a876139946a149ab4d9185574b98dc919d2eb6754f8abaa59d18b025637a3aa043b91817739554f4ed2026cf8022dbd83e351ce1fabc272841d2510a010140b4010dd48a617db09926f729e79c33ae0b4e94b79f04a1ae93ede6315eb3669de185a17d2b0ac9ee09fd4c64b678a0b61a0a86fa888a273c8511be83bfd6810f0247304402202b795e4de72646d76eab3f0ab27dfa30b810e856ff3a46c9a702df53bb0d8cc302203ccc4d822edab5f35caddb10af1be93583526ccfbade4b4ead350781e2f8adcd012102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f90141a3785919a2ce3c4ce26f298c3d51619bc474ae24014bcdd31328cd8cfbab2eff3395fa0a16fe5f486d12f22a9cedded5ae74feb4bbe5351346508c5405bcfee0020141ea0c6ba90763c2d3a296ad82ba45881abb4f426b3f87af162dd24d5109edc1cdd11915095ba47c3a9963dc1e6c432939872bc49212fe34c632cd3ab9fed429c4820141bbc9584a11074e83bc8c6759ec55401f0ae7b03ef290c3139814f545b58a9f8127258000874f44bc46db7646322107d4d86aec8e73b8719a61fff761d75b5dd9810065cd1d" } + }, { + "description": "Elements 2 input p2tr spend. Note this JSON is a different format from the previous example", + "given": { + "genesisBlockhash": "00902a6b70c2ca83b5d9c815d96a0e2f4202179316970d14ea1847dae5b1ca21", + "rawUnsignedTx": "020000000102d7e48759db4d6f3d3f38440162c487fcc95205b6500d8772a5b8c32e870de2280000000000fdffffff882358478a81a3259677612047587d24da92494ccfd4487dd3e27715bbb18bf60000000000fdffffff050abe40ef1f47c667beb32dd0488751358860d1433697e4787e142f066be1095f2d085d699b82683473a96932a016a46ebcce0c9eb442c9b5d42daf37e46d49ec90d603c868ed1a2a7dcf8405961581f13d091269a1710160392bf89d616ff06ae78cc117a914501d2552872ba1c5389433354a3e61d583eaf5ad870ac844723343c588eb47feef3c1bc1254b996613542085a3b679f9629cb7e06759083e1153bf03577a00445538d8b5ab25b295254efd5131a66d4a727d8da3ce37fd0347e524f0e7e781508d56be3df67dba0c7ec2494d9e8e4ac9c8d09c1c485a02d1225120ac73968281b866aacacc3f339549c3c8301d203e2d40cc33df78c17fd80389970a00adaa4b0390856e6570baed216bf61ca6bfe752ec81843655aa29f61c3559e50895ad406892a57f44dca170cb236a62bb874761250912430c0b8a3870373951760349f40321421e15d81278a86415548031b732409e8e938a85ed16dda97e0710f317a9146deab1ecba96723fabe6191924250a71b52321be870ad88338c4d86b698b3f8a90f2da77bf7ae70203bda44edf2d9bccbdb52d727efb09103056d5e5810a391118aa6db30e80a8812b95cf1436e46c069624676599cd7703b9b33c4e7e6ff5fc0ededc37a5a1f9db9b132745fdce0c1ce53871bd6e85e878225120c486c03619768c5a72a960a2246a7b750fbbbd27d35ed4d6d74a31ea856ea77d0125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a01000000000000122000000000000000000000000000006302000317307c438dde0f189eae26a2bf1bf48150d837313897607ef9cf7845a08ad2db0e31aa62d20b368c836dd8c76754691b852224dab5d4c9a9d68d967827d17300ee3fbbbe3887e295b821cd137d207b2069c2731c180ab5850ba49e3fd4969250fd4e10603300000000000000018b473501c52011ae3d0e20ea30e58000568d75d929e0322e7b48f8b5b1a51e4e1b816c73ad3e5b828a3e241d648603faab3f2bfe584ce269e01fa6a44662031dfd65d71c290c31c8b3768ed278a9f2dc39ac7b947f39fc07b128448873f00e34f465ac7c55632dcb2a8b3d45d903f1c6280b6e145dc40fb3130abc868184433dd35995a3293b3a7b8b83611b499e81587724f0c70e5a009a052f081793f661e4a2361c50491deeb03e19519ddf23c82454b4d422a7a9c5d79fd5e825f2554663668cb3d2b1fc1e72b48a057e7a4a4484b197318c2f13ab007c2648778ceb28f60ef1a756646a35e5c1efd5752641393570a5e03d9caca1569ffe38a3ae403ad8420b49104bac49d9e55482d0cf694483b20aa1a68904dc1f1550280583633e00387ec8df96aa163183255d099461a822234138688aeda7d95b4862877ae78b437fb81073909fb4457d281d0dcc467a23fbb3d31fdbcfb41777f903247366053cc247eeafd4e25b7764e2dca8e0266035c117b0c46dc7186a9b75e94b5c9c2baa4f66bdedd9866201ca82dfe6827568b057218768514c0d9782e687e98632f4c1d423aa16ca5bacbb2e211770ebc1d90cc7d15fff85fa667c3f8cac3daeb2dbcc6430a7cfa4ddd22e3f11a13ed97d77352d73a710b2948a7b359f8d7e6bc0586d784be01c12ffcfcab3cd6108e4c9c89d10e54e310621d6a2c9c6c21a5c71aca534945d5a50d3eaf6cc7d20596b1dfcef2021384d3ef46ed4c767bd3b491a80d9e8fedd66a78a3d37d18a7aa38ef8c68d124453f382020acc487f2ed1e3ea22de95b89b2882460f069c94449fd946938969a59eeaefc0a12b1701a22bee25be6d0a9ff8a380c1ac832a95bbf46235d516aa51ca98c8cc1942b5337ddee2fd3442aea1a0ebe433e029eac0098f701341e6052b1e46271656dc37e392b1bdcb1cb6f51c9e0e1eff425dd5ce487b0f07bf9b70b44139877c5f212ba55308c606b7e432b5ed1eda148d1548f0c6472eb93898d21ea70fa324bbe2ace95a11f5bc595223f1969e267afa6ca85a49312c4ed62bde5dc7d084598c9e3c599a4f2f8de95c2d594690a9b0a431fac27da409374a5b53d38accb05280e8f2b1df51870137132c12b1e805b7531d83b6aeff523cfb871e7abdbf54e0edf1e5ff2183bd3e9645322add1c82563f22a27307e5763bbd8ff2a26da86b6d3e62633240af7762797da73ae96398a758dca993b526da6de2842093f5f1c17c79e20e16b66de0a339a435f1cefed76d0ac98b870565c59940af0bec7e4df19807648a01cbce181404f34218c79401de4113df9f3e205418bbc845d138efea581f823d1c32af90713f035a4a5fe82e1b0f6645b4245f9c96dc12158cc0b9dcf58ca18d3c840804c949d26276bc65a241e311ecab2cdfcac3769324ad185c4f8e1390a46768d4c2c30372e006e6d536620e848f183a84b1bb9452e264b63c2af050c9dcca9bc3fb8589cd9703ff86f8ad014eb25da947085b802c2011ffb45855dbc69645f135614fcf78754db9a0b26be4fb5d8a6bde73b5e6c5ddcaf6b3070b1df1f6706d3812e12b0e1a5043ec3551c414c4dcadac7756a453e4f62d252893217b5b141402b18e966c169c126af8e296e408643084db6dc2876cbdd35b62c7ca446740223c4ef0ce9a20cdd523a135b29e2728e8b8f15e2d1ad16df332e60c5e38892ea28eaeebfb2fd9c9563cf1b4b65ca0feeeed3acf6ebb85dbc1f7bce884dd761e1680dbe55254388bae03f66cee8bc6ec8d7d7305f8861e4797aa9e8e098b559fc1112be542a282a38d448ce4dc3e1715a794f204a207dd47b311654d5599ace348b0d4b85806d0c0366e2dcc3abca5e1394230517d24025287be4c2bff6f1083ab46c21d145f947401b0fdbdb578d048ca9cf247517f3668ac71af5097c39e5b08edfd7933437c2f3ea039ad75d64b5adc7ec3d8ff233219b37bcd0b3bc3a1ba829d069db5e3fb60145a9e548817cb4b404d5a91a2fd9043555dd14baf31f9cce2504ba96430fc90090ca5e36db7c4af3be71ca44f7952f1353e5044da224d9627d4275e56c1ad4b93aad1a4a27df4f680b8044751c1e149543063ec912d3c1831364077c38f38bc999c017aab2a842c93416b7683e16cc4ae3d58b69bf6f4a07fab9dc2d63a44b65d890bbeda585826a77bcdd3133153fe0e83ec660323603a98eb97e19f07d4b40aaab5f58b30dfc29aeae63ee9eeafce594fbb0c3adeafe3e176c3eb042695a9f612350c1782cd9378dc3c1d85d63acf4fc58069fde087f4e50d9eca866a620d00ea3c024da328ff7eeb580c25969eab04a611f450fee3a99fb32b58ed63f28fc737e0569a67acac6ee5010f30f23c0e8a88893f629ccc60d576b92faa818f17b2eef7d2d6fa9b2503a710b784303b513a96756815f30e8d45b1795b8d1f5a686ccf9b3ac4708bd60fafa703669214914e5b65c6121e6f20f51cdfe34f05e4988a5b2340166615c32398e9ebf6909dafb12eb0bbd9c856f0fe84b3014f9b43aa9b4e9f6655ece31ac6f4444aef628ce43d4c2e27ba3888f11b51f3a515ba0fc8b45a2202c29bfe63c270c6efc73beba1cbf9fab52d85831893072093222f15f55ebf1620285481a89c1cf33264904d38ae2b8ce50b749d6719b22970ce8d96f61f119888772f5a4e1ed56c4735bedfc6bf7f4cd8dccbe3e7a39e2050551b1583083b2c5547c98ada570b9a95d14fed0c4f3b0d45b6f9f29d940d6623837b568c22035a2cbfde4d4b4898fefc4f390d5ed53b7857c150d4ecdbd41303f5949f2b396f76d02842b476755f2e595721630d7faf3e5f869a5903b5e70750cd1a199df822668a2567b1caccecdffe6c485855424c7271fbf3402d758d49f663d999556b43494393e675f4b4eb372ffb6d0225680cf6859b7741201d4a7e0c03a5ee10d073d78ddb72d3cad8ba064d5a7ff85a7d9dc76d5ce373cb7fcbe56b70fb130792e400e4ac5129f1dc02cb4b65a30354d57280c8bacf7d6859e9fea59d57cba2c0692f12aa596e09b00bf1c240a838c19a7815f4526aef8a96ce1ec745928d094cfd9049da6022833d71b72f0618813e71ecbfab66f50d254fde6e31557af61b2d677abc421ba41a6cafacf021a1a970a3f6411014c7087b4ce29fda27b25ae9ba30fb18cc26e6744eac902213c99821729821df94377288f464321e82a2fa7530b549afd687eb45a40a2d85f5fb475cd1daf501ca6511abb82d5854e91dd8b6176973537eee0f48ea6217f60b615498c0f2362992603199d23a988417e393d6de5ca281fc1aa75fe6744808f16eb39b95d6aebf26c2798dddc4dbd5762584207d633513c8e6f8c22cf63f1df26caf77f2dd88078b3d3dd7b88a65a45ebada6c4eef0f277cd609547d9910fd3a40188673740e85cf43c58abe64f66eb81f64fc2a2d58f9cff6d3cd0f9ac2a56942ade0d6ff847ae1ebb1c053d27a456a56badaaca62f38316e455ffabf02676da4b8bbd3b602216671a149a83238f385c65b085955c00160cd02c4a0262a39ea79909b2254ce36b4bd0d424fc801972c53fd862d644d16c96927622a67c5a8db7ed6e4befffb5e5af9557f2d0f3076047f14b06d7aa23406f2f9fa341a382082b65765ac10f5473d6c82f82c13cf230093bbc131daa3dc4580fd907c91eb518440fba44783c9c2d54c2835faba54aeafbcf7a57cc453c16844493154f85c19863b4e40557bc9957290191f74856b3dd663e5fc292acef35b72bde20cc6188499322e5d52d78d482cdbd8dde025fc8c93f6c3e7ee664394c74e6a80045c6e12423f0ee4dedbf0da58504343265bc56cc149abbdd5889d466e0bae9e510756f7d18b43427e12279fffcf433a6978e04c230d577955e4c24a6ef83669a685b61b96e8379bcdedce62333dce27a228af93148b673a792d8a2aab8e4de8ae50dabf6e3246aa5219577114c4d4544b9e44626be0d88730796bde1c1050440857e5b3a0f17a03f2476cc2590e20d055be994c3f34c0035ee5c4ab1bc630a52d3dc5bc240d5bd10a5a77f4541b5a0c4762b6657f52d7b33e9e682ac14af6d8e477eacb73451489b8931eff7bb8d3bcd056ea424455be97eb6b6c2ae8e75c8834730589f4a7e85b48bfb1dfdb8fef0555c076171f400bab26c51ccb839592ea65c7875f8f2a1dca7fe50f295f2e6f0b214cd0cec44024275d1a3eb211e5a5648e8d66f792ca94a77edf43ff939858ca2ca67987f4d3a3db71bbbbae1818716ab033dd8864cc6afdd3f6d5153acd3a1957bf841246dc5cfb304c6d93c6f1ff8e7a60516f35ee83649a63a88c076a301dd9c674627af8bbd89035ca8eeaf55dce2b427aa10c662cbae3140dfd4437ddbd3eff6798980ad32fb6ffc195d3117d765505f20d8987854d135679470d45c8cfecdc20e493509488d2dfd442e6277626e3acee945719d60c2689a59ff1c6349b57cd4a7fe66ab67eab11a3664ec67855b68046bed43df2ff494118a0a35bb7cefe2617b376bbc802500962bc3ba84e163b060866ec1a10fc78179d463a6eaa03b62eb1f4f7981775c35a5d419ca995cacaa85665ec2ec673ce9ef9e763b73217d45177baa7c519dd6fa9be812bac335832aa2c7ca732d5666416abf8c9d670ea2f24cc6f8c8820d8bfac9bb89e469bd7c25f1ea5fce4bdfa1bd294de54d61a0295313e54c1bf7dfac89e7d14b7b547dc9df5019883edc0a8d51d2675016c8614386ef3b30e8f0e6f8c465513e3cc0a4716a2174a8002b8b82e67f6a8bd9938a77cba18d3a42f2751e8e9d71d874c5b05fcbc33222710e418257d7c94a6a693d042c50bb2e47d78424cf95e9f2488dfd7d04ea0cec7785b27dec3fb64ca72549368116cf162fb6cb5af176757ef84dbc625ec53b211b41aa7446461d3fb78e49c08ea86eac1e396ad0e9764fb4ec44e613d6c9b92aaa8c317b4b17eb6f7915d501b92b2ef7d919b459d24d903f861aed3c1d69d49e6319c38de0b365dbea48a4ec2966da5d23b5207dfeba866661baa9f221a426346100d393eab9816bfb2850f1c51dc9c70bd43382115b23ecbb0399a6aec4907fdc5a908eced5c74d1ba490d37e65af40247f769680a7449f71a7b67a033b2048a02f87a2dda0bb66e11dd8d548aaf6e97abe971134d5bd4099da1d081500421b10e57499814b9f28433c5626d3f98a8578be88f0197c9e22db9262acd651ff75a54c2373b763dfea6780f1ecdcba1bc3c51a1ad9b85f500bebad48fdd38b41a54745adf0a5cb013e9e4a6da555000963ac79311485e221d79ad399bfb093d9d5de34030ebea9cba83da0b17afc28f3f1ae1ce3abc02282985f0df462701362574083c357d1611d1564b639253421af452341680111bfa747d64d7f57ebc4c682edabcf5b5f594405ba7210b925ace286fd91805dfe5b4d03c531d7df277810099737a9a24d4337908b5ec5188f571772a9bdd19e95c3a293cd26bf06aa400f2a79956c6b6f618e8f36b9765421a7c21fe5354cac9c6338a2be6d648b7adefe8c9f9217ff85100d67c6e11df953e2f78dc2547cd4b71f19f0d7e37a2425286c701ec7874e8612d8187a04f66fddf2738c077c22093624e19a15f364962348c1df9d57f7f013de1e018776649eba2075324b2f45f6a15088c17137bf7d62f663f0918f3da9d5ce42d5a05dac8573d056180bf5d25cfca186348c7c9d4bffb6b51f9b25ba978c38aa4da2ffb6165519b5470fda5d6ff972464c41532a368ec602abc6d691304d693eda048404c2c7bf7fb52a9cd5b1272dff486f23e94eff24eb58adf73780eb7d58e2c31d50ccfc2ef95b42612b83a066e868034706726ec65bac8b130bec693b812937082d68673451cdcc406d0f2b263020003fbd947fa1b1c1605c1d190f3d3e02d23683988f2d456975df01033883113898b17a9211633c2d4a92c1843af2a9f1b7edce71c3e3fd3e1003e7cd75f8678b1459259856941c65ef21d0249b3eace33006d73f19967dd5c2186ce0528160a3df3fd4e106033000000000000000186d73201c5236992ef55ce958c3edafdfbedb2a17ba129571ac56a7ac3505e7a561c92248c554283e01441f0bf6ee173c99eb6bbc7c4e6a10984d59041ea5301b9a3e7b5d1a6783427d6a9d3dfc0b0f125296e020fa98e08f0c05c5368b6e17c4b9e6d027aaf4ebd55506d15f040cf8ec4a47057b09c07b5ea7ad75a7e79f155cd035dca9f693961d1fa7a4bf8969e6c2a395ca3037d9bd5b33002d945c0cf1d9c62b0a664ff1ed28a7c1a25845a01493585c2452f2afaaba8cf239f7a1b87acc76f562e16de34f12ce9a2c6388fdad75e248b54bc6d80aa359eb8afbf64536d8d5f8eec633f8dd1cb6916f21922bc86df05834424f4ab4cd8779bbd53a9a8439b4de61d396cfa3d27d9741231ac7bd95a7a57f78f597dfbf534376502abd5cfcd08ee303f6816044df810706265f1f446335032f9d9286becc502ccd18ddabe4225d9a6d5c387f50a981ea706e14895897ca181346f14a7cc1c590450c1a459dcc2834eefe952441a518dd0792fbd4b5ce0ba17241bcabd1262f904e794f37325324ef9acb38e4956e5d25c1fd96f5fdf5f3dd071f6d44c696b6691284ebc68506f8c3263efd50b44fa50752c11f6d72b40fc5a988e81855c7be4571d54974f448c1f4aa497b185ec371fb0c2d210b7ff875a4a951fe780ebefde9b9d25ec1ad04e0a5c39acec4c4d8ea393d9793f5ef03f9f35ebe413229863875b7cc99e63bba8df0f200cbb914dc0287e563bcbab5317a19396274ad35f6749570e10a14a813d63aef04feafdee4551275d1f47e9440dedd9a48473e4b2f125309bcdedaee5f76365c7b936ff61de4b7ed402787b308c40d02a37ab6c1a481868c6b05d1e7d5297a37789a15d4c9824c895c635b3912cf459179fae84904e1f276f248806ca5b75b51ec27f58d5c370dbd6732c4434c97f0f05b31afb90704ddcf207a80d3e9d67d758f886b025ba7cbfb547503c122a3c311c3ae3376db1aabbe5e6c737cbe9645c3a463cbb20924e119177690234249ff2a862dbcfa1e7bd2a503901c6cd68924d233a435665b020c51c6733913e6fba3ca515e241ef9ab7987ed9d3122e730b8e7925d8648b2d9a3fd5df35d3e202ea1bf934bfc86120e37a20dac3cb1cd31b578c6d880057d93242fb7eff06ee8609ba7ae708c5f6d14cb8d27d6ab2e515d44eb5ca4eb04c7723145f9c20cb83694911c990daa423691b7b0fd2ba40bc2078725515e4a9ade5d6e706ed27f0700a848673655a41621c1dc0ef51ffc4a3ea309e63089a18c2812a1dbb43fde4a28847f4c21614eb654c87a1ae9ecc77a84b6126bc7a7f51314d01e7c979a1d30045f2a49b763ac7e93e80ba891c38e78c74ef8ceaf5ec1dc5378054a5636e10e9b326f5d88b44c58d1f7a482d35961f7995d89f23339f839a19017b88e6185886c8a9572cc2d21803d9082b0286b2f97ed88a2b3708a43cf2475122e9c95111a00c56ace7f607fd2cd294eb72a61ff5435177f57b39521c00ab332470553c2b2994caf891011cdf002ddd8a5188d0a5235b3bd9418d3ed5a3511d7e89110bac4c812f3ad50ea4e8c8c06ae7a19d5e6f51eaefe2cfe389f480e615f0ad12b8abbd999cefe81839f0876ee19924223d00f97492ab4a10eb7363eeeb9d261ddda0a507fa2ae5a6cc1a8deac23143290771ac9433ddb32e4563284f99ad6236d60b0db104a44c68b078f5deaa1c030914f5fe5f17431de34c0938517cd6f2128e3ecada06d1ca0f3c853f0157ea3c276042d4c668240a6cfd5235531c8e66c881583c7531612e5d672edc65b675e2aa550c511a4903cf43e454ce7efd93c6fe7fd7b153cf608823ee8783f83cbb3f1cfc23400dab55835ad3f18e1c9b5d6f78b3938ac0bc9fefe2273e7a82320d4e8c22c76979b366cc7257260539af1806d18de9422d0f12c3d4c6de6a5a20540d3eabbbce86b61e303475f681fb0f7b1f298bd922010b60645f08205869d473825a6e015ea2966f1542be053c53b84c13a95b7f006211822da08b661981d8acda443797d7630383758de098d746797666f1c81b19e517bad2ea6b938b00ea47a9d841b16d2887bf060d49d8a463afdd2ea61a24281c1fbc31efbcd05c764570967f81d4943d246356117229c611e9ad522539b28c930288cabe7ddc1cdf3bec0500b4e022982e5be0627879f8547a189b20e90092f10263cce9a11963cedc36fb2ea92836daacb71e39befa205f7a253e800784089dd6474fc36a2f8b12d230220c6d0dfacb13061651536cf725a057f5c7ca8cacaaa258deb2114a4c79a097ed34fbd47fbad54bc5330c5fff4bd76d5e77064272e34f3f13a87dcb6d60afb18678d12e6813a5307c3a4cfba49a59e7216f7168000a32cff1f5d98bb035aba353e91c9955d90993c9f6a2f967747c49e095b859dabbde023ad43989285ea7cd2297b62b73d3b89c9e79a62d67c11749bd0c7cc0158b097eea0a7cfe67680de87721b65158f51b8ec72a45363ce9d3efa028838e9ca0ee762d98a7b1838f152669fe233666943d5f51aedcfcdedf90826f17e71c7f3ab89a3f2a04ee459662a04e027e7dc8a9d251837831cf0a33bfd14db96dedb1ed4c858efa94b662689d58a8d315190308862667995155c8cb1a95d0490e6bbe8c807f76719a10e2395df1bebb6a844d19dc4d86c98b1327c1e14e2d466753515d3e7a3cd127057142a3af9e8340e3008828f0e1219c3668f52f6e4dd8a155b01e8b10744e716075aec04a49b438599fb30fecfd508cbdce837b73cf879d23722f07f2bd22b718349f3347d1db94eaf42e36c6b576c59adaef497d1406cd9621295397c58b44aa236bf724dbca317ef0c25fecd89c8501d66280f69b9c7d4029e0d0de3e031b4298ed1eb30e5bf67a90863953d10bcdeac2fa79a3e4fc1f560b9c4840911c981bdc378a0c2b38084f4bfe762fe17158c1e1ed8fb8079557048bf0b90409cf10be6ced7f99ccc8027bcadf3a592d252e38726ac4caaffbce70a2b2ca933eb83b9b4b767662e5602fac6a6c4aa8931ce1a5b42bbc78897e1ca416c30c54d6604532e25e76378e1d3e687ca17b2c8ac407801150b1b7b87597e8179cab00dbbe043437566bd44a9b3ce024ece11d56299bcbf536248b840e3ac9377114e1eda07019d797e97a7cad9ae0894038576bfc4222eadfe22216dedf354c1abf1bf14d341f2c542c0d78b4388b47fdc754bb1604dd47c7d39c50896e7e5facc4a003afec4d9a7d1664a39786bffa770b264e2f81ff4bd69429e14ecce868e5ef9c918d1de809ff88deeda5dfd2ab3c23e0618b0e945bf52504698bee195ed4617b2165d504dd36819d63d83eb8939fce034d7eea169cdfccaf740b3108245c1b543b808f331db3cef40e5167ba3f86aa4129eef869c71fa61c08fba948975172fa9ccc779bfaf4fdd4fdb09e6e7893fdcd1303dc69fc2fc47ba29e3cb7ccd1fb6a96c2af00f8f47acb5af41d6f321090934ef9f5da7c31aafa4e7aefca60e08ba23975490ed5d84b4e28e082238656a1ac6d7c003318f123d5e5431523622baef2afdfa833b581851f9997e6f0677928aa88b0fd06290f057ff079a213b732d6f98a4643cc3d5f5cd3c1b4814642527df40e92f0f967052f66b30fdb823b30cb8a23054b25214853866031033f0f0786f823788069076afab7f79664f1239f7cec40b70068e93ac98e2880238c019f093b1cfebe58bffcfa83da7fd6f6efe5bb4597e49ae0c57c3f2cfd9893281075664fea4876253871979f1805341eef192b0b7646df0dfac2710c55eaaa54e8fa0edbd6812a06c9feed2bebb538473e0fd751b2d50068e828c19e60fc920d3d09ac25e77d2ede88598d207b0e08017f5517c1ba52e2f03c33045abc40af91ee1c3b2033e9dfc066662799f19a2f1409e2351cf3083be5c0a64b97289e7d03c58bb21efc4333b3665cc7652d661aaed1518387ea0c0130fbf50cd01479316ab748f676c1e62b6197b98109f57cf6078ecdce2f0511227e01b5ce521ecaceb9873aaa832efbed393b416d278b33060ee08c7fa2c8eb71a2a3a03daf1104411000ab76ce93e10eb346d89e9f44215f14e819e63c91a1f56c6ae3799abe7d5cdaaeb79a3d97372e515210aab266a11fd017513c07647654d96cb016c5f93a4667ff93fcc9ba841df18a2d1f911b6ccf253db20d793ad73700de60125d36d2fd1490853ca3810c77562593bbf3fcaac0351b2f3bc1d5f1e09158c756bb008d9b1030b933d52a0d9520e8f33efc1b9c742ca0e580f836f33558cd5e4009d90ff18becd5461d37b8abc4ce357a47e91c2ca5ad46cb355d63cae6244e716a2a9d3c408b2561bc3c3c02b5b49ca1cb61d656d8cfd4c92718ce9363b34d916d22f2f9e0f6a7c2c2412dc399f49663d50a2e207cdcbd2bb70c927184f4d97d6b9e46ee303c60846c714d13070f9e5e7a68803f23a658a68c62a107c6a46d1393d28d2afbfeb6fd8b088f8a8489f64bd4a41d05b8999c0ac4243d1abd4aa137236f90354ecfa2433d5ebb90a04875c774ea8a04d74b0cdf170757a54780969c48de7962b669822d88d6ee88f408e3d3314fe35d9bcdb37e37d3e0ddb723ff0038af0a823e986d37482fc77f0876b6e22cc0671d5b3abd2d757e42e42bd4419d58a950d8c9de71e18b8f635a6ddd581486f14b1bf1da34cea5155809b304f1698b999d9f786e8510c90715024a9049d7e59e818abd7c307f86d60f3f17b5094fbeac1473154314dfbe7f8a79897abbb9bef9af0b53e5a86c88c0b3a60e9f265b965a5f4099776dbbfe11bb71aaa9789b3da19825df9f76430ebc49a6ce41f23e6db8dd73ddf305be76b72679c26036954df5e846ed5960dee98efb9d65d0e06e293c69803de5bf658c9e3143851da374475b88f6dbeda23076ce08b0b1f4ddbd5e94c6f527b0d8454997504c74010219d2e1ed79a6fd04da1ffe4ad19e6721f551e9c3276b4e1c006abdd876f4cffe1179f916833a77e3f71ebd6b563f8824df786d5f98678e4f287aba551d67ffcb30332f3dd6758053e6b8dc753bf602e5036d925518af274b41f4173890a0fb6a08cc479ba94848a3190019bc2cc2553764ec53ecfddbf7a792f0da5ea352ab1644482efb0f840543c72a2fb67ab06b704ef3a4911ae44fe0d550a57e39f16c4ad035e24aba5d22ba0b7231b036d52f3d650ae5cbd68f43303ed0c651b3bfb7d06fd8cabca0d33405a840eeba5a94a9d1d74deba1e5ff587f1b273e139c13a97a49e30e34ad0e7296024ad1aa8a6105ef5329abb5806c94b16759a609ce470edfd7b5c5a9628542aee0678071e28b2afcd037e54fc92a96a898b8f0905bdfc394f5ffbc2404133452746f8d9c438e86dbfd35c25da1bd80f0b9aa0126790ccd42bcea371040a6e1f543c6ce5446c95222dc483525427beb00d4d33e09257f703875ceeed702c68b315a560762277686228b7f2567fb9d6e306851b5fbdf228f47ef30233705e334c3edde1285807377c5af8c3ffa4987380e17ded5ce19fc396955a7d870bb9e5b45e2370eee8533263fd39c3513903e519a0b6d4c8bd75eeb397be473fcabc94b35bb3c87a8f8461aaa83cc0e343ff540f4e197f63c717a65035dff43e28da68749d2bee45783b2ca64afeb40c0f31ea7e85977e1cb6ca8b5223699b414768bb0fe628e2e3bf02e5d07a0bdee45cc7b6c0ef4642d8fb97ad9f77ed3ecc981db5f3a02cea95440970a21d2fa2fc90c093e541b70d3d1bec11f0a9436894599adcc5f1f8ddb9e179a29747b6b273c5b0942d0c37f424f84a1b2dd18af3fe3018f1ec65c99e632c7592a52b4c9396ce976ddf17f328ea0672716f2ec6d48a147edc03031d9851dab24ce7fee630eccea7088c0c642630200034d786c4c70316169588c04e4f7206a52eb01e07e910109a9b8dd0030a2e643896c59a1dab25086034dfe6984a64fa650a95576b7accbfbb1bb62664e7ad6e696ce2398b080b928f5fa06a4361ad870cafa5b2cf7cbe08ce032e9bea1d14dee10fd4e10603300000000000000010a681d008ddb1b1238cf5a46e76199f5139662813dddec6a57b4c91a1ba271087e42beeb6067080dc5d0b5c9205d65b8a6aaf250a4f4137c1fc33cce0e6e77b3cc923f0308de162b3830821fd8bf4bd4208e0123abb8c3c84ede176b905a1cfaa33fa4c02be83acc6ee5393708e5ab7eda2eac5fec183194e76cab1743a89bbe0c1dce8f7d66d9dec6eb6147c7ed42ac72266408e2b2f6fddc1dcb28ce7bc7412e7298f853325f3d665ae167a165afbb3c9ac0b0385ba8c292a3ed9cdefa8f9da87b2d4dc089adce1b53e5ff9aa888def60f14f081c0e9f465f5133814366323f99cd0fa97ab1baa4e26508e735f57e78f552283ad7e9f7eba6758c5f367e846d035802ae121e65cb6748750ec0465f8a8e4ea3f70f1da9896ad8735a48877af78961f940e8e9fa83002b2ffeb586f10e3ae00453a0259fab91970cc540acc8dac003a5c9bb2297c77d7c7d60afd9063664eace8c50ac5e4724e48dd090007557163fae9b79e64a030a81d94d1a2f6b8a1c8abec00db2ad240382d409ee7994716cfecad4574b9e8db72e8a63c08ec25606533f2832a8f12680c0c5ae28e841600ddff8694621347432b5f15fdff47af05de9cd4bb251af222ef39e3fbaa46faa31dfb02564f511d4e2007728295eea7901594f235154d745f42855f753dc981b2ec0074515a8e515114af3d199980e35b2649d5342a33cc74527db826b8256e74a5a3a81204ffd3c205ae31603519ff741b2774767414114c342c5033b8e2ba6a6644e74e40551b739851746fadaf3296f014dbfb6df72bd751f06c999c87ed34db3315d2e0607f173b6d0044dcb8cc8cc60dadb7d91898b9dde8474b7a393a11acfc4eaa788a8448bdf8126f064916c22233aab3942d6297e8c2a1e5231a20f272d0e0b1f9ea3a308ed96f88ff66f18bb8794721f5b6d690817787dccd39a7b2d205b0b145e319d92ef3ca0e8bba47debc29a8f4f77c5c0fc790522bd73abd526f9c157a452084ce77499e7be6f1553e51e3302600b52b070a2aef03ff62f07948ace88273073f08b7a852e7cc172f5b38bb4426bfa1718f137acd2ad75c15d059360aeaccccc3a7852004a39e792312db8d858b911ed064cedcc3d676b512ada00f1c534666757222919251637bf7f6653b18933155f5c5d2cf955725e56b0632dc5fb018a7311d954a92236672432b2512ab22307f28f4aff1ffbb9f2ed3aebdb57b7275a0d429737f0493ce071ae4463f624d69fdafa32c66e8d964d44cf23875b1e2396694afac493da162891d15f7d32fd502a7c08b2275b5e4f14aba32444a6ff40cdb375809cf3cebaa94a0dfd4ba288ac62242606a176370849b7638584d64d9ff365b5344e77f6ec60c706a2a87e7f3fefe6f73504297fbb41b2f1962d2c6d1a12fd76f9a1a598923396b3dfbea1a991a4ce6141e54cec960831251b676e54074335c8fbf9855322f2a44933ddbd5bea43e5a92b45edece83c86ab6d9d474cb13a1f326a658e61b5275ca8016dc0a209644885339fe5a6c0e3432d0ad9206adcd830be0511af156311600247d61e6e6f71a3c26374fc442f9e6e6c2becf5752ab986630f15d3b0324e015cfe05fe7de67ca5a822dc589ea2f8d284b3d7357e79de258466583d81e2aa117378d221bf004236528309d9ece0cfcd34ae0ffc473156aa8d8081289449d8d8de7d6ceec005ab79e637579eb79c1acc53f1ce58c992313433dae79312c712521c2a65a432453671b8fb387f87144fd57f4612e387b77f96e4734396a32d4d8911334bcc83e350a922772b447e12fa54c8f7cf94f7365ac28a5e0ef2c497282a197be05398b23a1b42f8275e36a2cee583fc0b22382ca1904b43d63169fcb58fd22c54651539effee7852f60ecdfc29baf5f8a7778e05bccf375d48a62f567c586bc580bbd0036ea2508c00731e160f5a3bb6b530367ff24f9c2d714cc04983b9c5378b33098b7274a8270e37a90e19e19ff4ceb3af40546424e8a08ae24afc8254324772b5c4825f0ec230201760151f971f69668a3d1bb591075d054935b685c088e05c6e6391199695b90daf349969b89c46f908d3c6f49b8edbe980a26a1e4164a56fea5456329d41da0b8ecdb74658ee55cead4ce17721ab5127b25af2b9a8a4cb9e02cb38503c1ddacc29668b4c5d9d99a74049b730d6791e486c7d510a7f4ed1e8cea648aa40465bb22aa4c5a6d448f0c3236617ee2a1dc18f75c4703c667655d88f8b4f6470a8bb0b212e8b5976e9a4484204db6090f6fb93afd273d0f143b3655b0b45376b573b3d453babb29eacef27e42751b79a986b4f05cfec8c9c73ff8d396e53a0e0d6a8156551e0f854984ea3609fd227d9b1930ee8e09a5a6b12c43b63e5ac8c9106c752622d549075693697d277400f6a3203f1e8b3c3fc368a013a1b6814bf41b05c7409dbaa95d8829ed6a5cbffadc128cade32961d1739e7ce48d66a9430f5b94001d39e0de8f026fcb3850d36aeebfcd6b1b02f43bc8b7753bcde20a318bc4d42554e133d32b7a7e9dd6057b0c17c1fa34ef0419a84ad0680c420ac39cf8c1f47ae55017c1d13845dc1ceadb8a0ef421a1d1f392f028b90e3e7d0aeff2777b2d6c57413441d9f2d0062c9d99e44bd26720317e20332456e726b9946f25d1dcf98396588686509a06b5068f2a70f9ff7f27e88261a762e0a8bac83b103494d40aceae4879a687e453b055af0f5cb3aa02547563921987a34bdea18c5a139dc41da0c3a61641d95be6c91c13c774d784a7e48ec0cbd2e212950191658767c07843c56fbaa12cf3388318bf03c3b2bdc782c9d6c57bf491246ff7a1bb02dcc7452a52eefd30014d6cf599fa0d3c55b660edf0effcd8bc18627bb58d704795b69f91215b9853dc2a0955bd9287da6e9a2263e0145c223aa941ce85192ba5d5ccaeb868160f5f71b497ff626d5b420b49b015905de5538437afada46136e9346e8cb266d7c9188f8fe1e75c39f2749b0d80c7ebef1406c80f33c362df1cfd52d7d1de42f714e9117207103f966f83a516e45cd79cd71238e294ef2de8eafe6104512fee9e2d9427ba658d3b8e0c02f60dbd66ceb3cb8b688dbe7cb531e7c0dbd0ac8018651520c968033e8c74de267859c1ecd3052712556e918257dcbf8337c666f563a79cd95c2d48cd83005ae10cea542044d25ef5656c42ece886a12596df42ce208b35ac86f25bb4819f79135196efee8a46ce7db683eef6a0680554964cca84407dbc35c68e88fe28df14c3addfa01786146f65803ecb31deea8aeef5a5f194a77e246c33911bd2b9ee1290a788c455bd4f71e405dd78a06c26013b4666717152a72d46796c07074e9b728946b18fe9f63c99fca30fd0c30c03c4f632aeaf06f1a75fa256d3f409b9d6890b9a0247de292e6eaaa1c41fbe955e33c4f3d4bc0b4a6f0171420f36ec93c70496e6d0a2c8de6a8717a324f94fc39e69d3bcc6d3b61d1e7331163c84b4da8eec4cf92dfb01285d64feb59ca35861ce6ba5871829a0f2e956f34b98c8503cabd2906da05c942e0140efd5669f891ca6abaa01e435d0de0d1afa9e2969a899118d70037ae811c992b33111ab422e0aa1a2cb3ec24ab57da300439cc9537ad37f540c2d6aaeb30169424b4484d5859953817e9139f0c254b683dd73b2cd770db2f7646ac91749f36c6dc7a6a0e22d01306bf3b81c867bdbe63ddb9e715cd3e1d933fe609422316c54745c938c61c48cfeb9b1986b28a0ab29d00aa03a5a5ebea9a2066c74cce8754779c98b6c9363a2e0d3aca909f05decc44c6136eb99eece52632bfe0490eea1bffddcf9e582c00e89c4f60b7a6d2c9ac3aa1dae8da2d86a3805c105bcf00a6e69689395e1d4d663240cfbd68a7ecce9ee04515d169629a4597eab8c00b3a03d6c68a5fffff6e73d4ea4600855b8ea0c8723dc9a3677711c3bbf068099e8d037f0bb71a0914305d88386d8cfb70cea5d31048da3f859f9a7a96b338fde70fd2217428daa93c73565dabfd9ca12fa8af18a89a1d95b1d9617d1812e5ceca0cf5410c610ce9e118e1796101bfe10cea9bf8aeebd876cc8f9ffb1fe41319a0d99e932c702662d93ce47c77c26b68b349f5f5c59bf4e691f16b8130a8f4eaa94802d85f77c8ad35d6a7bec2ec8597e4224ff497a90b70634cf0df2e03c3b21795e88a7a9f035946c556777944d8a231643b509b655a68449fd629b14d045cd3aa76903f10e69cbd6117ab4f0848c615e49cbb8d1af3d46552743cf0b67d2a1aca8ed4591e780e45c71449f6560cae7ca7cd771d2b556a20006da80fb0d2c91a28e7785e855c07b33892f9a5ba447c33af8c21c3add8531b94ca2266dba2859dc84430c5372fb968e4aa673df876e1dbf58d8dc5d70dac0fc58b7e13e4286a2e0ceb68c3f43e26e131b767c928df49958eb0a87bfb68c438a4a017f30996a4c6638f7f72f77d20ee5257d50fb4f12ce7cc8d256c7cbfc59fe52b2a784ca28d3a1960be4d15002530e27155fe641d6b03755a2dc0a16398c40088ab4e019a52ba79852091ccf3277f028812e4ad1ab7accbaaad51386457b1a13a4bd69dcbaba24f8479d8918ddb7e0e9d3f728a7981481efb4c1fe6dbd591d2da7fc9aab77cbdfb89326f33db3e4beee16710a936c5596a17c8da21c45f99fb2a735f47fab719009ac98a27787642d2b8cddae7cd280c414bad9db1c7e097a78b227dfd5695c7e8e1852f68c26ba55672ca198c229466ed479b0b54c5d7314bcbd51cbc6f6151ad652dc38650b5eedd0a28933c2afe7a1febc351823fe9a987cbf13e0d980129a38d7a4c8d6086315283db94032d84e833da53d72f14ee0b51f1e36b5d71c47d04356d1f075247fb600ac215206fd07d1b30bff1bd9b21bada456dd11a9ca6819b017311102a6562f9afdbcbee105fcace44e69c32c685e10997b10129e0cd1ebb6248296ee4ff8340529c78db64af42b7109b3e3b5577f3a5d8ea0f6b0eeb7eb8ab09caaa5dd79619709043ef5a92451f634e6f58625bd0cc1f8daf143471f7ed649f035149cabcef070da398eca0928a63dd4320e4f4f91b1385992dc036a8b1879221a0214b9c6db46d3a023707ca6f8749213b0c84963ca1b415929c3ec60f293deec9b694b691c826e5cc999582ed50c21fc94acc227b4f945e3cea5d92bdfcbb7822dca08a2e1b138e290b912cc4b5b1e14eecee115cd840d66aee031a12d87a2579a7fe139c21bc1b69a2e7d82e5920696ff37ad6db0284637e46f725b9a3147098a545bfe881e191bb4bfe532ca8973db2c525ff475615f179c3fc215e5750055f24d80f69474a1505eb1f45a87303959539322252bac4c0061ffa7b7dc5b11dcd35497f325c986650cf4bc2efab4ab2acce3be9f82f71d3b3b5a5e61c0c6fe8e788e23aa18a4ceaf35b3c42e270f2dd2a5f7e549bfca25d01ffa5b695d66b7fea8d9a8e19de471bc5d485c53c41a000796077440eecd94d902815917b7908cc49df5e4abdb7bd6d2c9b7730247c32cd658ffd87fc0d70bdac018811807ffa349e5a1e60fe265fda521e6e6096e2ddee666f86caa4ef2bed6e2ec9f57ce13ae3e5405b5493111e2af4996b73dce964498dd305b1123a898f771fce00772ded3f37f460e3521691b23441a23784eae2dafe57c8d3d8f5de081996b92bf188d639f2d50711539ed6651fbb3a737f352fe96f6a9306e559d0790e878acdc2e1623576558fc252efcefac922fb726f393b141f7d9a576ce2a06966b545477e5147fdaccb71e5ea44c830deb31a87f13385072385c332c1bb519140be7eda33e0442d8e311a94d30a08f3d60f24e812e2865e8947d692c921b68b7400c758fe43dbf8c37ea859278061544db0aa0755108e6f90cf5c4f2819c96302000327232096d1a82ecf61cbcefe3f6bb4b2cc570beab4cd6461a9d99d60e79d53137c6075e0dc4b055ad55aa8bce73a5eb67c780b3bb548c76957055d20ee484652dd0d58b06a9c5bc0453a6006553d7250b9c9378640113ec0529fe9a84aee08c0fd4e1060330000000000000001a17bd801fbecd6d44aa5c7dc813891fb97ad58d073e0f93b72396185b875617ea296d3feb204c8eae268302f29ef06c3f202a1efcaac3ff2f1097413b604b69b12973e197127108008cc904c93a533c3f1e057a21ceabf89bf3e63d38aa64511d918adf0fdd4896ee71bb9faf59c114bc44a2d1e9aa120bbd144f48e3fffde961e0e6db917ecaf6d677d49c58e63079c704b196b4c61690258dcc054f7f874b0f52b263753a611a4655acc1c98ce634efa94c66bb93f1d9434b68902be611796ec818027a6d43765dbda57dcc34f1aa5adabbb6865ace3421ab2d51eb62f5c732677e6bdd7aa7b8fb0789562fdf70770847b4b667c84a7cfd156aae89ab76d7035af3c588c4905fe0735d093d7e15d1ea576d21ad6a97590fc5313fd6451f4c627456c4cb8b49ab9ceee34ebf787abd148058bd47c890431aa513bd84f8accaaa59db7c8e412913959405a0d56ca54f364a68fb13ebb95fd10720b8bcf649817d23b9c151fcde394b63b25704c5f5f54f3486606b40fb0d1ee430b35949b87985c19f48498dcf4d52d7c61ebc2bf19846b6b0519fdc7758fb836bac55c686bc7d78850812753a51fb4da8eb8aa81cf676c43355ff5682796514935e0fc114a534f6cbd5198caaa04fef34c4e3105e6c80b7b0c7a3f9c78201ad7ae84eea0a7a8822c7966ddb0b63e67365eb42579da8120f31676619d5eedb33c075583f3dfd4de8fec0ff9f1ce0d1b0c64a9a14a848d6b8eb44371ded67dffb41137e9fb72210ff4574c345bd16e650d2e5c9d05847f0ccaceae3db51ca74227ededc05c203e7f89ce8a5e0d417c90542ac2df63e2c606c73020e33e3cbdbfda2fb75399722abdd43cfb4846d639bdb84d865055eae7e239c0cb071dde8c739ba2b9ae9887f94c4c94ce671de676883e3402e61d74c27622ec49b26747c6729f3ac7c28222470fb8af9bdfa664ae3f15b950a9fe62179ef1548448a739f4054b8e6d4a2d7092810314355fb5f419c8bd7a2c2f0a9cb822242e719c684755ef1bcce13328f6be317b9e8a1a86b5d3de65f288e5013b8eaf839de472b202c66db5dc9c974bba5b154a58a2f67f9fe33ad646b1c40c9cb0ef8c1715ec18a24acee922bf4f0c0fabc3e43edaf858ee24b23ad1a2c01801b0eb8a54b5b14776f80b9588d75e8102b49280e9a7e6b73a205599c1bde71ec85112bad7ed46981e2a7a5d5a7b67aa22e56b8cc2e1b67a155bc6767d35e7d95dab119ccbddd00058ba365b86511206a51fa56f4fcc838ad5a1f91d31826306acb351dc36fdf5e58026c65b160a3159abacf241896ad2a4bf1db73555c2a688b901c373b3ff986b19d1b42ee6f945e939cbac8eb5dc55d9ad247e954eb67b129d8a9625a2eac628b67cdca3acbf8333a27304e583957ab7ad11fb88db233b0372c5c7cc9a598b44bfd87ed64f6b80fe3564d9e24aa87cdbf5da3b01bf15e9ba1477286bcaee5ddd908682d24dc05e79b10ad6d9c27560c43f71d2d8bdafbb709c3561db25997ea5ae2c08f71148f50879cec08dead14638f415dd564d7c1d05455883a3a84c07d81770be34059e290fdd1a854e3d45de6b408dfd3ada2a05795ddc8a63c7a55a53fccf6986476ead6e703fd24262523f3069f56a48ef751c442ac901fd79b0af6676299dbfaf60b0a541f69e7482e9c8290e481ee98e14b2ac4de5c9eeb0601dbe3d19fe6539a84f405f9d5a432975a6c52aa397752b39ed7db65775c67a68a534c33fa61c22f9ad793ef0230fba4efd1a611a9776e39cf506f9b33b509dfa71242444106fb6b0c221125ccf82065aa06c6ead9e21eb0956ccfb406d71de0e9a53012bc52abc210294db90672f7d64eaa95eb122d5e477a79813a2db5e3982216b86dda6a2d11290db2b1af4fe693ab98b1647a7d934b192b254fff35ea441d965642b5c15d4af3c2996a627e101b379d477fc679632d74444e8d02f66f6b803d3bec62bcfcd738d0bae5a899e0ab85a124cbc6c0ac14956add647a0fe98a0ff007fbde6cfd5aa476ce0efe263cb742f3514ee1f859b15d9e45e8fc3bc6e905eb91817ddb1ccdbd7ed08099991139e43faa20bfd9577166d6099f28e1b69a5aaebe07f60ff642ed772a3430fec167ce63b71ae391b7924daa992e3137cc1d34c68b554a20bfbc50632356a5454246057f9f2a703fa7cbfd519e5a054d4bad914a5cd97a15cafbcb87a1c8f78119c6b8c08f06466c1dc863a5761b0c05f6535701689ec2e6f69893e84643ad41976fc9986ec505ef42aeaf0583807f3b2d713840e3636806962ad4a06460558a1bb04aed5c87a94ef1b9bb551a3a77df28ddc8a673ee2d5dd9f665703ae9619e5384b5bee10b3ca10be7e24d231b8de242d7f6fa759780d825e053c902d814a407408c46086b66c1e3c9844ecad6cb1df93b1f6730f03126eacf34fa5f8d2c2473802ec07f9c6a8f56d125ac59860240a57a5fe9d17903ce3c01d1e0a88798749a920092d44c57707e5c7f2431957424f3505a88f9fdb34b1ddbcb3ce25c82fbd22ae07eb5baeb5cf440fa94ce8e1059dae595fcf93fbf491fbaf032db776c62d41cb589a22e542c8db63962faf43221fd1fcbc47740f0252692a854e1436d159c53027d1c2e4a8a81523b999bddcbf56d6f5d94118c2a2d8c7edbe89132bfc50b1f95c45f24b9641a0eff4210fe5b1e1636b485b7e252084724e0a4a7b40547ab6a44500f8ebbf8a7ab1aeeca93373a479b70db6667ccbb4598d35c0891a3ca7535a9090c0894df776dcec823c8a5af8481ce2bacb857cfeebd8b08d738f00f93912f804561ea32d86928cf622dd250aa1e3751ce48389b80c425a53aec185efdb11e1196e473144a5a26b76cb31aece2f85fb4ad70c66e0dda82acd66373a28769de943705170dfe9eba298270ed6a280c8ee36f6135393d88b01396b78911f6b682bd99f23221cfec36aced43b4d4c52b872d29cd5ec4879fb43020fb3bf7ef7635d797021db7f3fb8ee334f2b30fee5ee5a666f3b154930418e090ffa39affeeb086a83baa760c47f3a3398389aa77698e829ac153c45ec9acc3d19d5b17a71e9fe7622d8133281083565bd71a32c5978cb7e91bb6afd83359d4217f9c50de6ea829099ea1fc011989e88c648c6a79f28f1733a11f7c1a7962cbaaf73710951e67279c79245063d1a0cc905781445c5bdc90979744287aa5b80f367dc17654bf3b5796cac1b027c72cdcf79b12b894e48f2d15c0c9880d64f4799af9a711001690d7c9e45cc5f1b6a9f2cd07390692f54dc6fe7e8584601f10ff5c33c4a4e72165ec5c4818109b89244b46814921bb231cffc5a281f98e4332835e1715038a1dbecd230f06f48119753da38784f5ffc5d7e7d89aa1bdbf47fbe3bb04e1b83da4199f6b0f4bc8e5dc58b7c961e0912fe9cb0184860eb5da860ae08e67f4dd1544269bbacddf2f17757e0d86f0d0dce2390aa2a9ff98dd4a65052d2bcb5a5ae103e6303ab8c92694e49df9b158b2b0e3938d2927c2be9fd2eeeb152d24d13cd4bb5af104fe856017d315a19348e8ec417ea0e7d0a650d845a562daad993db81dbc7138ff2c86172e3d2749f69f61d532fbd99f92897668f4ebd2d49d590cc2486c83cc9b8911286b6e610e430d3bb2c6125244e2aefe7cf124455ecb159be1df64066f36317ea8b4146836f71d351613c4e9d3c9f5f0c9f5cf99010697ea550d21bd74fcb9f9582c6b958122672fdb90183e446022710cdf9df0cef3e15f6f29025fcffc2ef15484df2b001cb919be46fd6821cd4f9c259859b600e61bb020338c9ce1ba362930165eec3340aadced8be80bb41db674f1215926e2104930a36a0873405d3d19dbd34f4383c5a96b4a363fa90f25da756768dbf289214db61632aaa0f0b4c39100c209f9059f8c840133d1412a7ee508f85f9286a7709a40b8bd1da7fae5906f5090e81a090a8ef689a639ac14b9f7e1182db7d6939c5cab757b2e6b5e384ece941c552f6f32dd5fc9d66752c1c1808531f31e039841f9cb610082b49fde69268aacd2c93db1af3e1ef1c2be6d124585979ee53bb39c4296f34356cb04fd3605a436dd39b6268e5213cff6f8429dd9eddb4602a6353e14b625f554d3472ea3afdd0353ebab6d80dc5ac2fa077d75a7d9ee224dd8a97a35abc1dcb4209fd4df8c8137a7e25bc46c6d961fe480da57638e9c82d5b98c0c4638aa18f2dddf26d52c6da811bf40739f604068f5375f21c14723d081a482b83d6e258dbffc12ac16cb53fba4934f3128f6b2444a7fa6582df34045564fdf17150977a4820a9c842ffc49cb4b112f17d6caef48a6b1fd8c15e61dcce152caef791c1b537f6bb2796bfc3961f3d5b1c487ab00e31399278fecac5ee64501079a2eb4b5c36175becdc2f0eb4f7ede7fa6591b4e508303137675dd8b61951f7585807e4b00d0c71ab87a7a34f314cc1208a306702a771110ac51c16e8ba7f68c5328f3dcddeb569351e8d69034a6b2c7c9d2cb42f9bcc6c67dad7d980c9c350d46c681f0c1498a2b9497cf30c5e8d6f1602107c52b8d07538579e2a5a5a8519c17d0466a6f5dd6a7e9c7267968e556d5728859122309dea1617b9f4d3c04c2d78388c7c00c38d01d17d619ebecc72e1e7be03ae1528b0a985ffe7f59ca0dd8c967bf13569b63e1fb093191986109522e0a942c560f14367d9f47672bbcdddd11a759042c0194e8e8924f5ba95cc2281ddf430fbe7873a0022d23b353a87f025562b86e3ab7fe5598e4349b62671ed0171763d75c972b0f7c1b6dcdf661ccf06d3b9f811021f7359cf472b20b49254e89945fbf7d86cc5e3863c415d61126de01401e5e3e1d268f3a2dd53edafb71cec77f0358bd1633194cfec66fab07c9f7232425dde93a97833a512967e50a21e515e053415ef42953c0fbfaecce64f723fd7c2ac67e5f2ce642455ba5de2fb14f6f4892d6fc6a173e2faab450e1a6db158a78ede3a1f9f4682275be675e82119a2139082d3f56afc000c3b72a644ae6d45389b1afa4eafa9037ecd3d52bbe469c714b58298f15850b2f8a31c7f03990dfccdfce485a12ef5179ab36c5774fcbc1c745b2062ea8a8550ea6fa8c9f97393137aa4b44fb3df98cbd4028c041cba5d82b1bb04f68f286853470e1e4c25136c29e2441292d0d50e100fd00406d3144304215e4390403c524fe017f4f9b2ad509f9b1d230be16b47d1813700b5a44cc5766510792a13191b2c033ee560f8cb369ee8525b5dff14a651130d5f3f36aaa67124cb3a2f9024783d4795611421167c9ca8d3783efd303591f5da1cee431d7eb425804325eed8c7e8241423669e6d0991d6db640c88d52c12177424bd50f56f2103b1dfea4648943b98c66152c12639dea44ffd06aac91ffd6398a8998ffa6cdb686cf2258399c0aa96cdda90a7d3561392ce061c6f2f7961ab8d6ec457aa08b3c60fabd338639e006a77f59b4d7a06c9d2125c92bddfa0b748da6f5d58f7e837f4f40968cbb9f55435cf086d20928834c314d84121c1dc9aaf40df8c26314dfa0a6ee2a64404a617b5f28fd84c9529025db4d14b4f2a8fd913cb341a8a1e75af6c344045d70acc3f9eb329e7454469ce2d5802a018e1924851267a8cb1477bccb8cc85f07cdd729fc15384b26b0e681821360266743f72a252ab0f4ff3b1bb81b4ea73fff7105df82c7c197594ccd192e0e07e562361f01d66b1d64a39bfb08b9d2ce909439879323d86096852b4ec291c6639bd37c16dc602bc56932ae88110deb95bbb2c0de30c5a890ca546587d482118d80ba07e4e099021aca2071f9988c955cd4c87c938cfd0b1d65f6a58401a1020b564864076146061f88f431849cb3e6fa76a98ec9fb5b80472677d292d9d2ef3b7168eb08a0000", + "utxosSpent": [ + { + "scriptPubKey": "5120900d1d75269396d4220c4529527dbcb746a6093c7209cea2d76a87c8ab9447fc", + "valueCommitment": "083696f3aefdf18c044cab5ef189c575d79a096db1c6bc17f152cdf1fd3cb136f3", + "assetCommitment": "0aacb400cb406593951f7f7e61b63f10db73ee63b861c31fd266cce84f145f87f2" + }, + { + "scriptPubKey": "5120b850370392dafc3e7d02db13b447c927e98b6439681fea582401e570904cd6a5", + "valueCommitment": "097dfabf3388968a388633a058784a5bff414a53801cf32109be34b92123d3096e", + "assetCommitment": "0ae983aaefe54f21571238e95a93a8c965ed737bc217ea480485be1e1969adc279" + } + ] + }, + "inputSpending": [ + { + "given": { + "txinIndex": 0, + "internalPrivkey": "fbfe896b002b1e98fc9f8aae4479760d3fd9f145bdaa9b6da59cb069cdc7539b", + "merkleRoot": null, + "hashType": 0 + }, + "intermediary": { + "internalPubkey": "03f658082d7f5ec466d61220aed1429391d7bedf8f03428c9d7e4062d80e37345a", + "sigHash": "2c478ce6d5637e0ea8be37a53090e0955b6c501773fccf6738520cfcc5442150" + } + }, + { + "given": { + "txinIndex": 1, + "internalPrivkey": "8547f4b1ccc5888353bb4781ab9a728fe0be2fdc30aa0db137d234723917b9b2", + "merkleRoot": null, + "hashType": 131 + }, + "intermediary": { + "internalPubkey": "03efb5fcad2eae616088aee84f5bf5ecc765cff8d7bcef65ec217eee817f0d1e91", + "sigHash": "00df8570ffea8d57bac49f979b64c6f0ddff24977608e7e8f9047a5ede03af96" + } + } + ] } ] } diff --git a/src/data/psbt.json b/src/data/psbt.json index b148c986a..b852ad74c 100644 --- a/src/data/psbt.json +++ b/src/data/psbt.json @@ -1102,12 +1102,24 @@ "result": "cHNidP8BAH0CAAAAAcxHwxyy9FpFa1AuYMltf+VRZ0Wb+xs7irBhENL3ZNDfAAAAAAD9////AqCGAQAAAAAAIlEg9bpXXraKrTowBOttg43mWTzNvrpNtwi247wAU/AVyx4hR/QFAAAAABYAFMtFjSqHFoz6HBMyvhQ92LQA8Ym8ZgAAAAABAIkCAAAAAYxqZIhs1zyGR1MCUaBZyQq4ug9tsyD8T3DZhrptncZQAAAAAAD9////AgDh9QUAAAAAIlEg57VlYkU0rn3hXaeIjADwG8K29OFka+lH9bfT0HQMm31bEBAkAQAAACJRIC+KLodP0myMhi9pydVeg4+NxhveBSpGNR29rfNGH3MPZQAAAAEBKwDh9QUAAAAAIlEg57VlYkU0rn3hXaeIjADwG8K29OFka+lH9bfT0HQMm30BAwQDAAAAARNBYqqZYa86n3RkuA32q8P5JOCzUjsif6fnfsUvgUbKvDI7+tac/nfHnggY1zJr0H0lyJ1RiENCkCEaCJBQpeheOwMhFiViK8ln+8oH7t8l+OupgyTlBVIgvrFwmImfEK24VhhDCQCYBc59AAAAAAAM/AlsaWdodG5pbmcEAgABACICA8qv05+H/b7q27AMU7Mq9crdV1eXrQRdyd+ftgOf/cF6CMtFjSoDAAAAAA==" }, { - "comment": "Taproot keyspend with no associated pubkey", + "comment": "PSBT Taproot keyspend with no associated pubkey", "privkeys": [ "cVd4tUYMjuWJS2te21dhjdBpTwvkxwdfjP9AC81PdM9BQwW7oaTo" ], "psbt": "cHNidP8BAH0CAAAAAd0d8Rfl45hnbKrHUO9vcDujNeb6VPuwyxDEVOwATVHyAQAAAAD9////AqCGAQAAAAAAIlEgeBUu8Q1HKJXIb76zd36Sl+izmyC/+w4AOsb6XLhNXUohR/QFAAAAABYAFFQykddI6WJF1YsFfGREBm/XYEGAZgAAAAABAIkCAAAAAfJC3TPe4ugqhUQW5CfoullkiEdvTl8MKBoWjZNHAFQSAAAAAAD9////AlsQECQBAAAAIlEgsO71o3RalUR0nhP9S0yNS4uncZHorNTFL2DmAdCyvHQA4fUFAAAAACJRIGApzC84K4A36zenkm3RSaYX9skR38q3j4G6qeZ0ffepZQAAAAEBKwDh9QUAAAAAIlEgYCnMLzgrgDfrN6eSbdFJphf2yRHfyrePgbqp5nR996kADPwJbGlnaHRuaW5nBAIAAQAiAgKm3O8QgEIUeJ+0Nman44ppKjC1A4tbrMC1YukyTtRdgAhUMpHXAwAAAAA=", "result": "cHNidP8BAH0CAAAAAd0d8Rfl45hnbKrHUO9vcDujNeb6VPuwyxDEVOwATVHyAQAAAAD9////AqCGAQAAAAAAIlEgeBUu8Q1HKJXIb76zd36Sl+izmyC/+w4AOsb6XLhNXUohR/QFAAAAABYAFFQykddI6WJF1YsFfGREBm/XYEGAZgAAAAABAIkCAAAAAfJC3TPe4ugqhUQW5CfoullkiEdvTl8MKBoWjZNHAFQSAAAAAAD9////AlsQECQBAAAAIlEgsO71o3RalUR0nhP9S0yNS4uncZHorNTFL2DmAdCyvHQA4fUFAAAAACJRIGApzC84K4A36zenkm3RSaYX9skR38q3j4G6qeZ0ffepZQAAAAEBKwDh9QUAAAAAIlEgYCnMLzgrgDfrN6eSbdFJphf2yRHfyrePgbqp5nR996kADPwJbGlnaHRuaW5nBAIAAQAiAgKm3O8QgEIUeJ+0Nman44ppKjC1A4tbrMC1YukyTtRdgAhUMpHXAwAAAAA=" + }, + { + "comment": "PSETv2 regtest p2tr keyspend, DEFAULT + SINGLE|ANYONECANPAY inputs", + "is_pset": true, + "master_xpriv": "tprv8gTfWnFCND72oJZfZTokBBXcS1FzQhrtd5wNFu3FgBE76yErH49cev2Zn3Wws3o6ZwKZVZaQP1UWKVNotpPg8U6tCgGrjMfaRQJvV1Vdbi7", + "privkeys": [ + "cW2Ybem38XTuRQ4PWDY7DJ1drUzBKVFmP3wSD2SV11HftBdP6aj5", + "cS3nM3rBgAMrSU6uMm44rAmGYvWyauhvKgBSx72ta78NEHfa8Jr1" + ], + "master_fully_signs": true, + "psbt": "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", + "result": "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" } ], "invalid_signer": [ diff --git a/src/descriptor.c b/src/descriptor.c index 1756653b5..1cae78926 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -2881,6 +2881,13 @@ int wally_descriptor_to_addresses(const struct wally_descriptor *descriptor, if (!(p = wally_malloc(descriptor->script_len))) return WALLY_ENOMEM; + if (descriptor->features & WALLY_MS_IS_ELEMENTS) { + /* Disable Elements address generation until: + * - It is reconciled with Elements-core, and + * - We support blinded addresses + */ + return WALLY_ERROR; + } memcpy(&ctx, descriptor, sizeof(ctx)); ctx.variant = variant; if (ctx.max_path_elems && diff --git a/src/psbt.c b/src/psbt.c index da6d23698..4d7c82ae5 100644 --- a/src/psbt.c +++ b/src/psbt.c @@ -10,6 +10,7 @@ #include "script_int.h" #include "script.h" #include "pullpush.h" +#include "tx_io.h" /* TODO: * - When setting utxo in an input via the psbt (in the SWIG @@ -1308,6 +1309,7 @@ int wally_psbt_free(struct wally_psbt *psbt) #ifdef BUILD_ELEMENTS wally_map_clear(&psbt->global_scalars); #endif /* BUILD_ELEMENTS */ + wally_psbt_signing_cache_disable(psbt); clear_and_free(psbt, sizeof(*psbt)); } return WALLY_OK; @@ -4422,47 +4424,63 @@ int wally_psbt_get_input_scriptcode(const struct wally_psbt *psbt, size_t index, return ret; } -/* Get the input scripts and values for taproot signing. - * Creates a non-value-owning map, avoiding allocating/copying the scripts. +static void append_signing_data(struct wally_map *m, size_t index, + unsigned char* bytes, size_t len) +{ + if (bytes && len) { + m->items[m->num_items].key = NULL; + m->items[m->num_items].key_len = index; + m->items[m->num_items].value = bytes; + m->items[m->num_items].value_len = len; + ++m->num_items; + } +} + +/* Get input scripts, assets (if applicable) and values for tx signing. + * Creates non-owning maps, avoiding allocations/copying. */ -static int get_scripts_and_values(const struct wally_psbt *psbt, - struct wally_map *scripts, - uint64_t **values) +static int get_signing_data(const struct wally_psbt *psbt, + struct wally_map *scripts, + struct wally_map *assets, + struct wally_map *values) { - size_t num_inputs = psbt->num_inputs, i; - int ret = WALLY_OK; + int ret; - wally_clear(scripts, sizeof(scripts)); + memset(scripts, 0, sizeof(*scripts)); + memset(values, 0, sizeof(*values)); + if (assets) + memset(assets, 0, sizeof(*assets)); - if (!(*values = wally_malloc(num_inputs * sizeof(uint64_t)))) - return WALLY_ENOMEM; - if (!(scripts->items = wally_calloc(num_inputs * sizeof(struct wally_map_item)))) { - ret = WALLY_ENOMEM; - goto fail; - } - scripts->items_allocation_len = num_inputs; + ret = wally_map_init(psbt->num_inputs, NULL, scripts); + if (ret == WALLY_OK) + ret = wally_map_init(psbt->num_inputs, NULL, values); + if (ret == WALLY_OK && assets) + ret = wally_map_init(psbt->num_inputs, NULL, assets); - for (i = 0; i < num_inputs && ret == WALLY_OK; ++i) { + /* We add all the data we have and let the signing code + * validate that it is sufficient, since the required data + * depends on things like the sighash type being signed with. + */ + for (size_t i = 0; i < psbt->num_inputs && ret == WALLY_OK; ++i) { const struct wally_psbt_input *p = psbt->inputs + i; const struct wally_tx_output *utxo = utxo_from_input(psbt, p); - if (!utxo || !utxo->script) - ret = WALLY_EINVAL; - else { - (*values)[i] = utxo->satoshi; /* FIXME: Support for Elements */ - /* Add the script to the map without allocating/copying */ - scripts->items[i].key_len = i; - scripts->items[i].value = utxo->script; - scripts->items[i].value_len = utxo->script_len; + if (utxo) { + /* Add items to maps without allocating/copying */ + append_signing_data(scripts, i, utxo->script, utxo->script_len); + if (assets) { + append_signing_data(assets, i, utxo->asset, utxo->asset_len); + append_signing_data(values, i, utxo->value, utxo->value_len); + } else { + append_signing_data(values, i, (unsigned char*)&utxo->satoshi, + sizeof(utxo->satoshi)); + } } } - if (ret == WALLY_OK) - scripts->num_items = num_inputs; - else { + if (ret != WALLY_OK) { wally_free(scripts->items); /* No need to clear the value pointers */ - wally_clear(scripts, sizeof(scripts)); -fail: - wally_free(*values); - *values = NULL; + wally_free(values->items); + if (assets) + wally_free(assets->items); } return ret; } @@ -4473,19 +4491,22 @@ int wally_psbt_get_input_signature_hash(struct wally_psbt *psbt, size_t index, uint32_t flags, unsigned char *bytes_out, size_t len) { - struct wally_map scripts; const struct wally_psbt_input *inp = psbt_get_input(psbt, index); - const bool is_taproot = is_taproot_input(psbt, inp); - uint64_t satoshi, *values = NULL; - uint32_t sighash, sig_flags; + const struct wally_tx_output *utxo = utxo_from_input(psbt, inp); size_t is_pset; + uint32_t sighash, sig_flags; + const bool is_taproot = is_taproot_input(psbt, inp); int ret; - if (!inp || !tx || flags) + if (!tx || !inp || !utxo || flags) return WALLY_EINVAL; if ((ret = wally_psbt_is_elements(psbt, &is_pset)) != WALLY_OK) return ret; +#ifndef BUILD_ELEMENTS + if (is_pset) + return WALLY_EINVAL; /* Unsupported */ +#endif sighash = inp->sighash; if (!sighash) @@ -4493,41 +4514,44 @@ int wally_psbt_get_input_signature_hash(struct wally_psbt *psbt, size_t index, else if (sighash & 0xffffff00) return WALLY_EINVAL; + if (is_taproot) { + struct wally_map scripts, assets, values; + struct wally_map *assets_p = is_pset ? &assets : NULL; + +#ifdef BUILD_ELEMENTS + if (is_pset && mem_is_zero(psbt->genesis_blockhash, sizeof(psbt->genesis_blockhash))) + return WALLY_EINVAL; /* Genesis blockhash is required for taproot */ +#endif + ret = get_signing_data(psbt, &scripts, assets_p, &values); + if (ret == WALLY_OK) + ret = wally_tx_get_input_signature_hash(tx, index, + &scripts, assets_p, &values, + NULL, 0, 0, WALLY_NO_CODESEPARATOR, + NULL, 0, + psbt->genesis_blockhash, sizeof(psbt->genesis_blockhash), + sighash, WALLY_SIGTYPE_SW_V1, + psbt->signing_cache, bytes_out, len); + + wally_free(scripts.items); /* No need to clear the value pointers */ + wally_free(values.items); + if (assets_p) + wally_free(assets_p->items); + return ret; + } + sig_flags = inp->witness_utxo ? WALLY_TX_FLAG_USE_WITNESS : 0; - if (is_pset) { - const struct wally_tx_output *utxo = utxo_from_input(psbt, inp); - if (!utxo) - return WALLY_EINVAL; /* Prevout is required */ #ifdef BUILD_ELEMENTS + if (is_pset) return wally_tx_get_elements_signature_hash(tx, index, script, script_len, utxo->value, utxo->value_len, sighash, sig_flags, bytes_out, len); -#else - return WALLY_EINVAL; /* Unsupported */ -#endif /* BUILD_ELEMENTS */ - } - - if (!is_taproot) { - satoshi = inp->witness_utxo ? inp->witness_utxo->satoshi : 0; - return wally_tx_get_btc_signature_hash(tx, index, script, script_len, - satoshi, sighash, sig_flags, - bytes_out, len); - } - - /* Taproot */ - if ((ret = get_scripts_and_values(psbt, &scripts, &values)) == WALLY_OK) { - ret = wally_tx_get_btc_taproot_signature_hash(tx, index, &scripts, - values, psbt->num_inputs, - NULL, 0, 0, 0xFFFFFFFF, - NULL, 0, sighash, 0, - bytes_out, len); - wally_free(values); - wally_free(scripts.items); /* No need to clear the value pointers */ - } - return ret; +#endif + return wally_tx_get_btc_signature_hash(tx, index, script, script_len, + utxo->satoshi, sighash, sig_flags, + bytes_out, len); } int wally_psbt_sign_input_bip32(struct wally_psbt *psbt, @@ -4545,7 +4569,7 @@ int wally_psbt_sign_input_bip32(struct wally_psbt *psbt, int ret; if (!inp || !hdkey || hdkey->priv_key[0] != BIP32_FLAG_KEY_PRIVATE || - (flags & ~EC_FLAGS_ALL)) + (flags & ~(EC_FLAG_GRIND_R|EC_FLAG_ELEMENTS))) return WALLY_EINVAL; /* Find the public key this signature is for */ @@ -4561,24 +4585,25 @@ int wally_psbt_sign_input_bip32(struct wally_psbt *psbt, if (ret != WALLY_OK || !pubkey_idx) return WALLY_EINVAL; /* Signing pubkey key not found */ - /* Copy signing key so we can tweak it if needed */ - memcpy(signing_key, hdkey->priv_key + 1, EC_PRIVATE_KEY_LEN); - - if (is_taproot) { + if (!is_taproot) { + /* ECDSA: Use untweaked private key. Only grinding flag is relevant */ + memcpy(signing_key, hdkey->priv_key + 1, EC_PRIVATE_KEY_LEN); + flags = EC_FLAG_ECDSA | (flags & EC_FLAG_GRIND_R); + } else { /* Schnorr BIP340: Tweak the private key */ const struct wally_map_item *p = wally_map_get_integer(&inp->psbt_fields, PSBT_IN_TAP_MERKLE_ROOT); const unsigned char *merkle_root = p ? p->value : NULL; const size_t merkle_root_len = p ? p->value_len : 0; - ret = wally_ec_private_key_bip341_tweak(signing_key, sizeof(signing_key), + + ret = wally_ec_private_key_bip341_tweak(hdkey->priv_key + 1, EC_PRIVATE_KEY_LEN, merkle_root, merkle_root_len, - 0, signing_key, sizeof(signing_key)); + flags & EC_FLAG_ELEMENTS, + signing_key, sizeof(signing_key)); if (ret != WALLY_OK) goto done; - flags = EC_FLAG_SCHNORR; - } else { - /* ECDSA: Only grinding flag is relevant */ - flags = EC_FLAG_ECDSA | (flags & EC_FLAG_GRIND_R); + /* Only Elements flag is relevant */ + flags = EC_FLAG_SCHNORR | (flags & EC_FLAG_ELEMENTS); } sighash = inp->sighash; @@ -4626,12 +4651,18 @@ int wally_psbt_sign_bip32(struct wally_psbt *psbt, struct wally_tx *tx; if (!hdkey || hdkey->priv_key[0] != BIP32_FLAG_KEY_PRIVATE || - (flags & ~EC_FLAGS_ALL)) + (flags & ~EC_FLAG_GRIND_R)) return WALLY_EINVAL; if ((ret = psbt_build_tx(psbt, &tx, &is_pset, false)) != WALLY_OK) return ret; +#ifdef BUILD_ELEMENTS + if (is_pset) { + flags |= EC_FLAG_ELEMENTS; + } +#endif + /* Go through each of the inputs */ for (i = 0; ret == WALLY_OK && i < psbt->num_inputs; ++i) { unsigned char txhash[WALLY_TXHASH_LEN]; @@ -4691,6 +4722,24 @@ int wally_psbt_sign(struct wally_psbt *psbt, return ret; } +int wally_psbt_signing_cache_enable(struct wally_psbt *psbt, uint32_t flags) +{ + if (!psbt || flags) + return WALLY_EINVAL; + wally_psbt_signing_cache_disable(psbt); + return wally_map_init_alloc(TXIO_CACHE_INITIAL_SIZE, NULL, + &psbt->signing_cache); +} + +int wally_psbt_signing_cache_disable(struct wally_psbt *psbt) +{ + if (!psbt) + return WALLY_EINVAL; + wally_map_free(psbt->signing_cache); + psbt->signing_cache = NULL; + return WALLY_OK; +} + static const struct wally_map_item *get_sig(const struct wally_psbt_input *input, size_t i, size_t n) { diff --git a/src/swig_java/swig.i b/src/swig_java/swig.i index ac1e77e74..03ff33716 100644 --- a/src/swig_java/swig.i +++ b/src/swig_java/swig.i @@ -955,6 +955,8 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) { %returns_void__(wally_psbt_sign); %returns_void__(wally_psbt_sign_bip32); %returns_void__(wally_psbt_sign_input_bip32); +%returns_void__(wally_psbt_signing_cache_enable); +%returns_void__(wally_psbt_signing_cache_disable); %returns_string(wally_psbt_to_base64); %returns_size_t(wally_psbt_to_bytes); %returns_array_(wally_ripemd160, 3, 4, RIPEMD160_LEN); @@ -1015,6 +1017,7 @@ static jobjectArray create_jstringArray(JNIEnv *jenv, char **p, size_t len) { %returns_array_(wally_tx_get_btc_signature_hash, 8, 9, SHA256_LEN); %returns_array_(wally_tx_get_btc_taproot_signature_hash, 14, 15, SHA256_LEN); %returns_array_(wally_tx_get_elements_signature_hash, 9, 10, SHA256_LEN); +%returns_array_(wally_tx_get_input_signature_hash, 17, 18, SHA256_LEN); %returns_size_t(wally_tx_get_elements_weight_discount); %returns_array_(wally_tx_get_hash_prevouts, 4, 5, SHA256_LEN); %returns_array_(wally_tx_get_input_blinding_nonce, 3, 4, SHA256_LEN); diff --git a/src/swig_python/python_extra.py_in b/src/swig_python/python_extra.py_in index 08db803cb..bd433b43a 100644 --- a/src/swig_python/python_extra.py_in +++ b/src/swig_python/python_extra.py_in @@ -236,6 +236,7 @@ tx_get_btc_signature_hash = _wrap_bin(tx_get_btc_signature_hash, SHA256_LEN) tx_get_btc_taproot_signature_hash = _wrap_bin(tx_get_btc_taproot_signature_hash, SHA256_LEN) tx_get_hash_prevouts = _wrap_bin(tx_get_hash_prevouts, SHA256_LEN) tx_get_input_script = _wrap_bin(tx_get_input_script, tx_get_input_script_len) +tx_get_input_signature_hash = _wrap_bin(tx_get_input_signature_hash, SHA256_LEN) tx_get_input_txhash = _wrap_bin(tx_get_input_txhash, WALLY_TXHASH_LEN) tx_get_input_witness = _wrap_bin(tx_get_input_witness, tx_get_input_witness_len) tx_get_output_script = _wrap_bin(tx_get_output_script, tx_get_output_script_len) diff --git a/src/test/test_psbt.py b/src/test/test_psbt.py index 74a2eacc3..a488c7656 100644 --- a/src/test/test_psbt.py +++ b/src/test/test_psbt.py @@ -167,6 +167,7 @@ def do_sign(self, case): expected_ret = WALLY_OK if expected else WALLY_EINVAL priv_key, priv_key_len = make_cbuffer('00'*32) psbt = self.parse_base64(case['psbt']) + wally_psbt_signing_cache_enable(psbt, 0) # Enable signing cache for wif in case['privkeys']: self.assertEqual(WALLY_OK, wally_wif_to_bytes(wif, 0xEF, 0, priv_key, priv_key_len)) self.assertEqual(expected_ret, wally_psbt_sign(psbt, priv_key, priv_key_len, FLAG_GRIND_R)) @@ -176,24 +177,34 @@ def do_sign(self, case): if expected and case.get('master_xpriv', None): # Test signing with the master extended private key. # Note we cannot check for equality with the explicit private keys - # since the PSBTs contain multiple keys from the same master, - # and only some of them are given as explicit private keys. + # in all cases, since the PSBTs contain multiple keys from the same + # master, and some test cases only give a subset as explicit private keys. key_out = POINTER(ext_key)() ret = bip32_key_from_base58_alloc(case['master_xpriv'], byref(key_out)) self.assertEqual(ret, WALLY_OK) psbt = self.parse_base64(case['psbt']) + wally_psbt_signing_cache_enable(psbt, 0) # Enable signing cache ret = wally_psbt_sign_bip32(psbt, key_out, 0x4) - b64_out = self.roundtrip(psbt) - self.assertNotEqual(b64_out, case['psbt']) # Inputs have been signed + # If all of the explicit private keys resulting from the master xpriv + # are present, we can verify the fully signed result matches exactly + can_match = case.get('all_privkeys_present', False) + b64_out = self.roundtrip(psbt, expected if can_match else None) + if not can_match: + # Check that the result changed at least, i.e. some inputs were signed + self.assertNotEqual(b64_out, case['psbt']) bip32_key_free(key_out) def test_signer_role(self): """Test the PSBT signer role""" + _, is_elements_build = wally_is_elements_build() + for case in JSON['signer']: - self.do_sign(case) + if is_elements_build or not case.get('is_pset', False): + self.do_sign(case) for case in JSON['invalid_signer']: - self.do_sign(case) + if is_elements_build or not case.get('is_pset', False): + self.do_sign(case) def test_finalizer_role(self): """Test the PSBT finalizer role""" diff --git a/src/test/test_sign.py b/src/test/test_sign.py index aadd38de2..bdd63921b 100755 --- a/src/test/test_sign.py +++ b/src/test/test_sign.py @@ -406,7 +406,7 @@ def add_by_final_vbf(a_hex, b_hex): def test_bip340_sigs(self): num_tests_run = 0 - sig_out, sig_out_len = make_cbuffer('00' * EC_SIGNATURE_LEN) + out, out_len = make_cbuffer('00' * EC_SIGNATURE_LEN) for case in self.get_bip340_sign_cases(): priv_key, priv_key_len = make_cbuffer(case['priv_key']) @@ -416,13 +416,12 @@ def test_bip340_sigs(self): sig, sig_len = make_cbuffer(case['sig']) is_valid = case['is_valid'] - if priv_key_len == EC_PRIV_KEY_LEN: num_tests_run += 1 flags = FLAG_SCHNORR - ret = self.sign(priv_key, msg, flags, sig_out, None, aux_rand) + ret = self.sign(priv_key, msg, flags, out, None, aux_rand) self.assertEqual(ret, WALLY_OK) - self.assertEqual(sig, sig_out) + self.assertEqual(sig, out) ret = wally_ec_sig_from_bytes_len(priv_key, priv_key_len, msg, msg_len, FLAG_SCHNORR) @@ -433,7 +432,10 @@ def test_bip340_sigs(self): msg, msg_len, FLAG_SCHNORR) self.assertEqual(ret, (WALLY_EINVAL, 0)) - # TODO support wally_ec_public_key_from_private_key with bip340 keys to check priv_key -> pub_key + ret = wally_ec_public_key_from_private_key(priv_key, priv_key_len, out, EC_PUBLIC_KEY_LEN) + self.assertEqual(ret, WALLY_OK) + # pub_key is x-only, so ignore the leading byte to compare + self.assertEqual(out[1:EC_PUBLIC_KEY_LEN].hex(), case['pub_key'].lower()) ret = wally_ec_sig_verify(pub_key, pub_key_len, msg, msg_len, FLAG_SCHNORR, sig, sig_len) self.assertEqual(ret == WALLY_OK, is_valid) diff --git a/src/test/test_transaction.py b/src/test/test_transaction.py index 85338caef..bf7fc1198 100644 --- a/src/test/test_transaction.py +++ b/src/test/test_transaction.py @@ -10,6 +10,9 @@ TX_FAKE_HEX = utf8('010000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000') TX_HEX = utf8('0100000001be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396000000008b483045022100da43201760bda697222002f56266bf65023fef2094519e13077f777baed553b102205ce35d05eabda58cd50a67977a65706347cc25ef43153e309ff210a134722e9e0141042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9ffffffff0123ce0100000000001976a9142bc89c2702e0e618db7d59eb5ce2f0f147b4075488ac00000000') TX_WITNESS_HEX = utf8('020000000001012f94ddd965758445be2dfac132c5e75c517edf5ea04b745a953d0bc04c32829901000000006aedc98002a8c500000000000022002009246bbe3beb48cf1f6f2954f90d648eb04d68570b797e104fead9e6c3c87fd40544020000000000160014c221cdfc1b867d82f19d761d4e09f3b6216d8a8304004830450221008aaa56e4f0efa1f7b7ed690944ac1b59f046a59306fcd1d09924936bd500046d02202b22e13a2ad7e16a0390d726c56dfc9f07647f7abcfac651e35e5dc9d830fc8a01483045022100e096ad0acdc9e8261d1cdad973f7f234ee84a6ee68e0b89ff0c1370896e63fe102202ec36d7554d1feac8bc297279f89830da98953664b73d38767e81ee0763b9988014752210390134e68561872313ba59e56700732483f4a43c2de24559cb8c7039f25f7faf821039eb59b267a78f1020f27a83dc5e3b1e4157e4a517774040a196e9f43f08ad17d52ae89a3b720') +SIGTYPE_PRE_SW = 1 +SIGTYPE_SW_V0 = 2 +SIGTYPE_SW_V1 = 3 # Test vectors from: # https://github.com/bitcoin/bips/blob/master/bip-0341/wallet-test-vectors.json @@ -19,9 +22,11 @@ class TransactionTests(unittest.TestCase): - def tx_deserialize_hex(self, hex_): + def tx_deserialize_hex(self, hex_, is_elements=False): tx_p = pointer(wally_tx()) - self.assertEqual(WALLY_OK, wally_tx_from_hex(hex_, 0x0, tx_p)) + USE_ELEMENTS = 2 # WALLY_TX_FLAG_USE_ELEMENTS + flags = USE_ELEMENTS if is_elements else 0 + self.assertEqual(WALLY_OK, wally_tx_from_hex(hex_, flags, tx_p)) return tx_p[0] def tx_serialize_hex(self, tx): @@ -314,29 +319,23 @@ def test_get_signature_hash(self): (tx, 0, script, script_len, 1, 1, 16, out, out_len), # Invalid flags (tx, 0, script, script_len, 1, 1, 0, None, out_len), # Empty bytes (tx, 0, script, script_len, 1, 1, 0, out, 31), # Short len - ]: + ]: self.assertEqual(WALLY_EINVAL, wally_tx_get_btc_signature_hash(*args)) - def sha256d(hex_): - bin_input, bin_input_len = make_cbuffer(hex_) - buf, buf_len = make_cbuffer('00'*32) - self.assertEqual(WALLY_OK, wally_sha256d(bin_input, bin_input_len, buf, buf_len)) - return h(buf) - script, script_len = make_cbuffer('00') out, out_len = make_cbuffer('00'*32) for args, expected in [ ((tx, 0, script, script_len, 1, 1, 0, out, out_len), utf8('1bcf681d585c3cbbc64b30a69e60b721fc0aacc57132dfbe43af6df8f4797a80')), - ((tx, 1, script, script_len, 1, 1, 0, out, out_len), - utf8('01'+'00'*31)), - ((tx, 0, script, script_len, 1, 0, 0, out, out_len), - utf8('882630e74173c928fc18236b99e25ffd15643faabc65c010e9ca27b8db29278a')), - ((tx, 0, script, script_len, 1, 1, 1, out, out_len), - utf8('5dad88b42332e3559950b325bba69eedb64b9330e55585fc1098964572f9c45d')), - ((tx, 0, script, script_len, 0, 1, 1, out, out_len), - utf8('bb30f5feed35b2591eedd8e778d507236a756e8c2eff8cf72ef0afa83abdea31')), - ]: + ((tx, 1, script, script_len, 1, 1, 0, out, out_len), + utf8('01'+'00'*31)), + ((tx, 0, script, script_len, 1, 0, 0, out, out_len), + utf8('882630e74173c928fc18236b99e25ffd15643faabc65c010e9ca27b8db29278a')), + ((tx, 0, script, script_len, 1, 1, 1, out, out_len), + utf8('5dad88b42332e3559950b325bba69eedb64b9330e55585fc1098964572f9c45d')), + ((tx, 0, script, script_len, 0, 1, 1, out, out_len), + utf8('bb30f5feed35b2591eedd8e778d507236a756e8c2eff8cf72ef0afa83abdea31')), + ]: self.assertEqual(WALLY_OK, wally_tx_get_btc_signature_hash(*args)) self.assertEqual(expected, h(out[:out_len])) @@ -405,7 +404,8 @@ def test_hash_prevouts(self): self.assertEqual(WALLY_EINVAL, wally_get_hash_prevouts(*args)) def test_bip341_tweak(self): - """Tests for computing the bip341 signature hash""" + """Tests for computing a public/private bip341 key tweak""" + _, is_elements_build = wally_is_elements_build() pubkey_cases = [] mc = lambda h: (None, 0) if h is None else make_cbuffer(h) @@ -422,6 +422,34 @@ def test_bip341_tweak(self): ret = wally_ec_public_key_bip341_tweak(*args) self.assertEqual(ret, WALLY_OK) self.assertEqual(expected, h(bytes_out[1:out_len])) + if is_elements_build: + # Verify that Elements tweaking produces a different tweak. + # We don't have test vectors; the actual values are tested + # by taproot signing tests elsewhere. + args[4] = 0x10 # EC_FLAG_ELEMENTS + ret = wally_ec_public_key_bip341_tweak(*args) + self.assertEqual(ret, WALLY_OK) + self.assertNotEqual(expected, h(bytes_out[1:out_len])) + + # Invalid args + ((k, k_len), (m, m_len), _) = pubkey_cases[1] + self.assertTrue(m_len != 0) # We require a test case with merkle data + invalid_args = [ + [None, k_len, m, m_len, 0, bytes_out, out_len], # NULL pubkey + [k, 0, m, m_len, 0, bytes_out, out_len], # Empty pubkey + [k, 65, m, m_len, 0, bytes_out, out_len], # Uncompressed pubkey + [k, 15, m, m_len, 0, bytes_out, out_len], # Invalid pubkey length + [k, k_len, None, m_len, 0, bytes_out, out_len], # NULL merkle + [k, k_len, m, 0, 0, bytes_out, out_len], # Empty merkle + [k, k_len, m, 15, 0, bytes_out, out_len], # Invalid merkle length + [k, k_len, m, m_len, 3, bytes_out, out_len], # Unknown flags + [k, k_len, m, m_len, 0, None, out_len], # NULL output + [k, k_len, m, m_len, 0, bytes_out, 0], # Empty output + [k, k_len, m, m_len, 0, bytes_out, 15], # Invalid output length + ] + for args in invalid_args: + ret = wally_ec_public_key_bip341_tweak(*args) + self.assertEqual(ret, WALLY_EINVAL) privkey_cases = [] mc = lambda h: (None, 0) if h is None else make_cbuffer(h) @@ -438,11 +466,34 @@ def test_bip341_tweak(self): ret = wally_ec_private_key_bip341_tweak(*args) self.assertEqual(ret, WALLY_OK) self.assertEqual(expected, h(bytes_out[:out_len])) + if is_elements_build: + # Verify that Elements tweaking produces a different tweak, as above + args[4] = 0x10 # EC_FLAG_ELEMENTS + ret = wally_ec_private_key_bip341_tweak(*args) + self.assertEqual(ret, WALLY_OK) + self.assertNotEqual(expected, h(bytes_out[:out_len])) - # FIXME: Add invalid arguments cases for pub/priv keys + # Invalid args + ((k, k_len), (m, m_len), _) = privkey_cases[1] + self.assertTrue(m_len != 0) # We require a test case with merkle data + invalid_args = [ + [None, k_len, m, m_len, 0, bytes_out, out_len], # NULL private key + [k, 0, m, m_len, 0, bytes_out, out_len], # Empty private key + [k, 15, m, m_len, 0, bytes_out, out_len], # Invalid private key length + [k, k_len, None, m_len, 0, bytes_out, out_len], # NULL merkle + [k, k_len, m, 0, 0, bytes_out, out_len], # Empty merkle + [k, k_len, m, 15, 0, bytes_out, out_len], # Invalid merkle length + [k, k_len, m, m_len, 3, bytes_out, out_len], # Unknown flags + [k, k_len, m, m_len, 0, None, out_len], # NULL output + [k, k_len, m, m_len, 0, bytes_out, 0], # Empty output + [k, k_len, m, m_len, 0, bytes_out, 15], # Invalid output length + ] + for args in invalid_args: + ret = wally_ec_private_key_bip341_tweak(*args) + self.assertEqual(ret, WALLY_EINVAL) - def test_get_taproot_signature_hash(self): - """Tests for computing the taproot signature hash""" + def test_get_btc_taproot_signature_hash(self): + """Tests for computing the BTC taproot signature hash""" keyspend_case = JSON['keyPathSpending'][0] input_spending = keyspend_case['inputSpending'] @@ -454,7 +505,7 @@ def test_get_taproot_signature_hash(self): values = (c_uint64 * num_utxos)() num_values = num_utxos # Bad/Faked data for invalid parameter checks - empty_scripts = pointer(wally_map()) + empty_map = pointer(wally_map()) non_tr_scripts = pointer(wally_map()) wally_map_init_alloc(num_utxos, None, non_tr_scripts) fake_script, fake_script_len = make_cbuffer('00') @@ -504,12 +555,12 @@ def test_get_taproot_signature_hash(self): [(0, None)], # NULL tx [(1, 50)], # Invalid index [(2, None)], # NULL scripts - [(2, empty_scripts)], # Missing script(s) + [(2, empty_map)], # Missing script(s) + [(2, non_tr_scripts)], # Non-taproot script (for the input being signed) [(3, None)], # NULL values [(4, 0)], # Missing values [(4, 1)], # Too few values [(5, fake_script)], # Zero-length tapleaf script - [(5, non_tr_scripts)], # Non-taproot input script [(6, fake_script_len)], # NULL tapleaf script [(7, 2)], # Invalid key version (only 0/1 are allowed) [(9, fake_annex)], # Zero length annex @@ -529,6 +580,116 @@ def test_get_taproot_signature_hash(self): ret = wally_tx_get_btc_taproot_signature_hash(*args) self.assertEqual(ret, WALLY_EINVAL) + def test_get_elements_taproot_signature_hash(self): + """Tests for computing the Elements taproot signature hash""" + _, is_elements_build = wally_is_elements_build() + if not is_elements_build: + self.skipTest('Elements support is disabled') + + keyspend_case = JSON['keyPathSpending'][1] + input_spending = keyspend_case['inputSpending'] + utxos = keyspend_case['given']['utxosSpent'] + genesis = bytes.fromhex(keyspend_case['given']['genesisBlockhash']) + genesis, genesis_len = make_cbuffer(bytes(reversed(genesis)).hex()) + num_utxos = len(utxos) + + def make_map(n): + m = pointer(wally_map()) + wally_map_init_alloc(n, None, m) + return m + + scripts, values, assets = [make_map(num_utxos) for i in range(3)] + # Bad/Faked data for invalid parameter checks + empty_map = pointer(wally_map()) + non_tr_scripts = pointer(wally_map()) + wally_map_init_alloc(num_utxos, None, non_tr_scripts) + fake_script, fake_script_len = make_cbuffer('00') + fake_annex, fake_annex_len = make_cbuffer('5000') + bad_annex, bad_annex_len = make_cbuffer('00') + + for i, utxo in enumerate(utxos): + for k, m in [ + ('scriptPubKey', scripts), + ('assetCommitment', assets), + ('valueCommitment', values) + ]: + v, v_len = make_cbuffer(utxo[k]) + wally_map_add_integer(m, i, v, v_len) + + wally_map_add_integer(non_tr_scripts, i, fake_script, fake_script_len) + + tx = self.tx_deserialize_hex(keyspend_case['given']['rawUnsignedTx'], True) + bytes_out, out_len = make_cbuffer('00'*32) + + for input_index in range(len(input_spending)): + sighash = input_spending[input_index]['given']['hashType'] + index = input_spending[input_index]['given']['txinIndex'] + expected = utf8(input_spending[input_index]['intermediary']['sigHash']) + + # Unused in these tests + tapleaf_script = None + tapleaf_script_len = 0 + key_version = 0 + codesep_pos = 0xFFFFFFFF + flags = SIGTYPE_SW_V1 + annex = None + annex_len = 0 + + args = [tx, index, scripts, assets, values, tapleaf_script, tapleaf_script_len, + key_version, codesep_pos, annex, annex_len, genesis, genesis_len, + sighash, flags, None, bytes_out, out_len] + + self.assertEqual(wally_tx_get_input_signature_hash(*args), WALLY_OK) + self.assertEqual(out_len, 32) + self.assertEqual(expected, h(bytes_out[:out_len])) + + # Test that signing with a provided tapleaf script/annex works + args[5] = fake_script + args[6] = fake_script_len + self.assertEqual(wally_tx_get_input_signature_hash(*args), WALLY_OK) + args[9] = fake_annex + args[10] = fake_annex_len + self.assertEqual(wally_tx_get_input_signature_hash(*args), WALLY_OK) + + # Invalid args + invalid_cases = [ + [(0, None)], # NULL tx + [(1, 50)], # Invalid index + [(2, None)], # NULL scripts + [(2, empty_map)], # Missing script(s) + [(2, non_tr_scripts)], # Non-taproot script (for the input being signed) + [(3, None)], # NULL assets + [(3, empty_map)], # Missing asset(s) + [(3, scripts)], # Invalid asset(s) + [(4, None)], # NULL values + [(4, empty_map)], # Missing values(s) + [(4, scripts)], # Invalid values(s) + [(5, fake_script)], # Zero-length tapleaf script + [(6, fake_script_len)], # NULL tapleaf script + [(7, 2)], # Invalid key version (only 0/1 are allowed) + [(8, 1)], # Code separator position given (TODO: Implement) + [(9, fake_annex)], # Zero length annex + [(10, fake_annex_len)], # NULL annex + [(9, bad_annex), (10, bad_annex_len)], # Missing 0x50 annex prefix + [(11, None)], # NULL genesis blockhash + [(12, 0)], # Empty genesis blockhash + [(12, 16)], # Invalid genesis blockhash len + [(13, 0xffffffff)], # Invalid sighash + [(14, 0xff)], # Unknown flag(s) + [(16, None)], # NULL output + [(17, 0)], # Zero length output + [(17, 33)], # Incorrect length output + ] + for case in invalid_cases: + args = [tx, index, scripts, assets, values, tapleaf_script, tapleaf_script_len, + key_version, codesep_pos, annex, annex_len, genesis, genesis_len, + sighash, flags, None, bytes_out, out_len] + + for i, arg in case: + args[i] = arg + ret = wally_tx_get_input_signature_hash(*args) + self.assertEqual(ret, WALLY_EINVAL) + if __name__ == '__main__': unittest.main() diff --git a/src/test/util.py b/src/test/util.py index 6cdbadaf3..071c25d70 100755 --- a/src/test/util.py +++ b/src/test/util.py @@ -193,7 +193,9 @@ class wally_psbt(Structure): ('has_fallback_locktime', c_uint32), ('tx_modifiable_flags', c_uint32), ('global_scalars', wally_map), - ('pset_modifiable_flags', c_uint32)] + ('pset_modifiable_flags', c_uint32), + ('genesis_blockhash', c_ubyte * 32), + ('signing_cache', POINTER(wally_map))] for f in ( # Internal functions @@ -629,6 +631,8 @@ class wally_psbt(Structure): ('wally_psbt_sign', c_int, [POINTER(wally_psbt), c_void_p, c_size_t, c_uint32]), ('wally_psbt_sign_bip32', c_int, [POINTER(wally_psbt), POINTER(ext_key), c_uint32]), ('wally_psbt_sign_input_bip32', c_int, [POINTER(wally_psbt), c_size_t, c_size_t, c_void_p, c_size_t, POINTER(ext_key), c_uint32]), + ('wally_psbt_signing_cache_disable', c_int, [POINTER(wally_psbt)]), + ('wally_psbt_signing_cache_enable', c_int, [POINTER(wally_psbt), c_uint32]), ('wally_psbt_to_base64', c_int, [POINTER(wally_psbt), c_uint32, c_char_p_p]), ('wally_psbt_to_bytes', c_int, [POINTER(wally_psbt), c_uint32, c_void_p, c_size_t, c_size_t_p]), ('wally_ripemd160', c_int, [c_void_p, c_size_t, c_void_p, c_size_t]), @@ -691,6 +695,7 @@ class wally_psbt(Structure): ('wally_tx_get_elements_signature_hash', c_int, [POINTER(wally_tx), c_size_t, c_void_p, c_size_t, c_void_p, c_size_t, c_uint32, c_uint32, c_void_p, c_size_t]), ('wally_tx_get_elements_weight_discount', c_int, [POINTER(wally_tx), c_uint32, c_size_t_p]), ('wally_tx_get_hash_prevouts', c_int, [POINTER(wally_tx), c_size_t, c_size_t, c_void_p, c_size_t]), + ('wally_tx_get_input_signature_hash', c_int, [POINTER(wally_tx), c_size_t, POINTER(wally_map), POINTER(wally_map), POINTER(wally_map), c_void_p, c_size_t, c_uint32, c_uint32, c_void_p, c_size_t, c_void_p, c_size_t, c_uint32, c_uint32, POINTER(wally_map), c_void_p, c_size_t]), ('wally_tx_get_length', c_int, [POINTER(wally_tx), c_uint32, c_size_t_p]), ('wally_tx_get_signature_hash', c_int, [POINTER(wally_tx), c_size_t, c_void_p, c_size_t, c_void_p, c_size_t, c_uint32, c_uint64, c_uint32, c_uint32, c_uint32, c_void_p, c_size_t]), ('wally_tx_get_total_output_satoshi', c_int, [POINTER(wally_tx), c_uint64_p]), diff --git a/src/transaction.c b/src/transaction.c index 066569df2..5aeb8ac31 100644 --- a/src/transaction.c +++ b/src/transaction.c @@ -10,6 +10,7 @@ #include #include "pullpush.h" +#include "script.h" #include "script_int.h" #define WALLY_TX_ALL_FLAGS \ @@ -33,17 +34,6 @@ static const unsigned char DUMMY_SIG[EC_SIGNATURE_DER_MAX_LEN + 1]; /* +1 for si #define MAX_INVALID_SATOSHI ((uint64_t) -1) -#define EXT_FLAG_BIP342 0x1 /* Indicates BIP342 tapscript message extension */ - -#ifdef BUILD_ELEMENTS -#define TAPLEAF(is_elements) (is_elements) ? "TapLeaf/elements" : "TapLeaf" -#define TAPSIGHASH(is_elements) (is_elements) ? "TapSighash/elements" : "TapSighash" -#else -#define TAPLEAF(is_elements) "TapLeaf" -#define TAPSIGHASH(is_elements) "TapSighash" -#endif - - /* Extra options when serializing for hashing */ struct tx_serialize_opts { @@ -56,17 +46,6 @@ struct tx_serialize_opts bool bip143; /* Serialize for BIP143 hash */ const unsigned char *value; /* Confidential value of the input we are signing */ size_t value_len; /* length of 'value' in bytes */ - bool bip341; /* Serialize for BIP341 taproot hash */ - unsigned char ext_flag; /* 1 = serialize for BIP342 tapscript hash */ - const uint64_t *prev_satoshis; /* Output amounts for BIP341/342 sha_amounts */ - size_t num_prev_satoshis; /* Number of items in prev_satoshis */ - const struct wally_map *scripts; /* Output scripts for BIP341/342 sha_scriptpubkeys */ - const unsigned char *tapleaf_script; /* Executed BIP342 tapscript */ - size_t tapleaf_script_len; /* Length of executed tapscript */ - uint32_t key_version; /* BIP342 key version */ - uint32_t codesep_position; /* BIP342 codeseparator position */ - const unsigned char *annex; /* Annex data to be put under sighash */ - size_t annex_len; /* Length of sighash data, including 0x50 prefix */ }; static const unsigned char EMPTY_OUTPUT[9] = { @@ -132,21 +111,12 @@ static bool is_valid_elements_tx_input_pegin(const struct wally_tx_input *input) return is_valid_elements_tx_input(input) && (input->features & WALLY_TX_IS_PEGIN); } -static bool is_null_bytes(const unsigned char *bytes, size_t bytes_len) -{ - size_t i; - for (i = 0; i < bytes_len; ++i) - if (bytes[i]) - return false; - return true; -} - static bool is_coinbase_bytes(const unsigned char *bytes, size_t bytes_len, uint32_t index) { - return index == 0xffffffff && is_null_bytes(bytes, bytes_len); + return index == 0xffffffff && mem_is_zero(bytes, bytes_len); } -static bool is_valid_coinbase_input(const struct wally_tx_input *input) +static bool is_coinbase_input(const struct wally_tx_input *input) { return input && is_coinbase_bytes(input->txhash, sizeof(input->txhash), input->index); } @@ -1666,25 +1636,38 @@ int wally_tx_get_witness_count(const struct wally_tx *tx, size_t *written) return WALLY_OK; } -static int get_txout_commitments_size(const struct wally_tx_output *output, - size_t *written) +static size_t txout_get_serialized_len(const struct wally_tx_output *output, + bool is_elements, size_t *witness_size_out) { -#ifdef BUILD_ELEMENTS - size_t c_n; + size_t n; + const bool output_is_elements = output->features & WALLY_TX_IS_ELEMENTS; - if (!(*written = confidential_asset_length_from_bytes(output->asset))) - return WALLY_EINVAL; - if (!(c_n = confidential_value_length_from_bytes(output->value))) - return WALLY_EINVAL; - *written += c_n; - if (!(c_n = confidential_nonce_length_from_bytes(output->nonce))) - return WALLY_EINVAL; - *written += c_n; + if (witness_size_out) + *witness_size_out = 0; + if (is_elements != output_is_elements) + return 0; + if (!is_elements) + n = sizeof(output->satoshi); + else { +#ifndef BUILD_ELEMENTS + return 0; #else - (void)output; - *written = 0; -#endif - return WALLY_OK; + size_t sub_n; + if (!(n = confidential_asset_length_from_bytes(output->asset))) + return 0; + if (!(sub_n = confidential_value_length_from_bytes(output->value))) + return 0; + n += sub_n; + if (!(sub_n = confidential_nonce_length_from_bytes(output->nonce))) + return 0; + n += sub_n; + if (witness_size_out) { + *witness_size_out = varbuff_get_length(output->rangeproof_len) + + varbuff_get_length(output->surjectionproof_len); + } +#endif /* BUILD_ELEMENTS */ + } + return n + varbuff_get_length(output->script_len); } static int get_txin_issuance_size(const struct wally_tx_input *input, @@ -1713,17 +1696,6 @@ static int get_txin_issuance_size(const struct wally_tx_input *input, return WALLY_OK; } -/* Get the (exact) BIP341 serialized tx size as per BIP341/342/118 */ -static size_t get_btc_bip341_size(const struct tx_serialize_opts *opts) -{ - const bool sh_anyonecanpay = opts->tx_sighash & WALLY_SIGHASH_ANYONECANPAY; - const bool sh_none = (opts->tx_sighash & WALLY_SIGHASH_MASK) == WALLY_SIGHASH_NONE; - /* Note the leading 1 is for the sighash epoch byte */ - return 1 + 174 - (sh_anyonecanpay ? 49 : 0) - (sh_none ? SHA256_LEN : 0) + - (opts->annex_len ? SHA256_LEN : 0) + - (opts->ext_flag == EXT_FLAG_BIP342 ? SHA256_LEN + 1 + 4 : 0); -} - /* We compute the size of the witness separately so we can compute vsize * without iterating the transaction twice with different flags. */ @@ -1735,7 +1707,7 @@ static int tx_get_lengths(const struct wally_tx *tx, size_t n, i, j; const unsigned char sighash = opts ? opts->sighash : 0; const bool sh_anyonecanpay = sighash & WALLY_SIGHASH_ANYONECANPAY; - const bool sh_rangeproof = sighash & WALLY_SIGHASH_RANGEPROOF; + const bool sh_rangeproof = is_elements && (sighash & WALLY_SIGHASH_RANGEPROOF); const bool sh_none = (sighash & WALLY_SIGHASH_MASK) == WALLY_SIGHASH_NONE; const bool sh_single = (sighash & WALLY_SIGHASH_MASK) == WALLY_SIGHASH_SINGLE; @@ -1750,12 +1722,6 @@ static int tx_get_lengths(const struct wally_tx *tx, if (flags & WALLY_TX_FLAG_USE_WITNESS) return WALLY_ERROR; /* Segwit tx hashing uses bip143 opts member */ - if (opts->bip341) { - *base_size = get_btc_bip341_size(opts); - *witness_size = 0; - return WALLY_OK; - } - if (opts->bip143) { size_t issuance_size, amount_size = sizeof(uint64_t); @@ -1766,7 +1732,7 @@ static int tx_get_lengths(const struct wally_tx *tx, varbuff_get_length(opts->script_len) + /* script */ sizeof(uint32_t) + /* input sequence */ SHA256_LEN + /* hash outputs */ - ((is_elements && sh_rangeproof) ? SHA256_LEN : 0) + /* rangeproof */ + (sh_rangeproof ? SHA256_LEN : 0) + /* rangeproof */ sizeof(uint32_t) + /* nlocktime */ sizeof(uint32_t); /* tx sighash */ @@ -1836,21 +1802,11 @@ static int tx_get_lengths(const struct wally_tx *tx, if (sh_single && i != opts->index) n += sizeof(EMPTY_OUTPUT); else { - if (is_elements && (output->features & WALLY_TX_IS_ELEMENTS)) { - size_t commit_size; - if (get_txout_commitments_size(output, &commit_size) != WALLY_OK) - return WALLY_EINVAL; - n += commit_size; - } else - n += sizeof(output->satoshi); - n += varbuff_get_length(output->script_len); - -#ifdef BUILD_ELEMENTS - if (is_elements && sh_rangeproof) { - n += varbuff_get_length(output->rangeproof_len) + - varbuff_get_length(output->surjectionproof_len); - } -#endif /* BUILD_ELEMENTS */ + size_t wit_size = 0, *wit_p = sh_rangeproof ? &wit_size : NULL; + size_t txout_len = txout_get_serialized_len(output, is_elements, wit_p); + if (!txout_len) + return WALLY_EINVAL; /* Error getting txout length */ + n += txout_len + wit_size; } } } @@ -2092,11 +2048,11 @@ int wally_tx_get_hash_prevouts(const struct wally_tx *tx, return hash_prevouts(buff_p, inputs_size, bytes_out, len, inputs_size > sizeof(buff)); } -static inline int tx_to_bip143_bytes(const struct wally_tx *tx, - const struct tx_serialize_opts *opts, - uint32_t flags, - unsigned char *bytes_out, size_t len, - size_t *written) +static int tx_to_bip143_bytes(const struct wally_tx *tx, + const struct tx_serialize_opts *opts, + uint32_t flags, + unsigned char *bytes_out, size_t len, + size_t *written) { unsigned char buff[TX_STACK_SIZE / 2], *buff_p = buff; size_t i, inputs_size, outputs_size, rangeproof_size = 0, issuances_size = 0, buff_len = sizeof(buff); @@ -2126,47 +2082,21 @@ static inline int tx_to_bip143_bytes(const struct wally_tx *tx, outputs_size = 0; else if (sh_single) { const struct wally_tx_output *output = tx->outputs + opts->index; - if (!is_elements) - outputs_size = sizeof(uint64_t) + - varbuff_get_length(output->script_len); -#ifdef BUILD_ELEMENTS - else { - if ((ret = get_txout_commitments_size(output, &outputs_size)) != WALLY_OK) - goto error; - outputs_size += varbuff_get_length(output->script_len); - - if (sh_rangeproof) { - rangeproof_size = varbuff_get_length(output->rangeproof_len) + - varbuff_get_length(output->surjectionproof_len); - } - } -#else - else - return WALLY_EINVAL; -#endif + size_t wit_size = 0, *wit_p = sh_rangeproof ? &wit_size : NULL; + outputs_size = txout_get_serialized_len(output, is_elements, wit_p); + if (!outputs_size) + goto error; /* Error getting txout length */ + rangeproof_size += wit_size; } else { outputs_size = 0; for (i = 0; i < tx->num_outputs; ++i) { const struct wally_tx_output *output = tx->outputs + i; - if (!is_elements) - outputs_size += sizeof(uint64_t); -#ifdef BUILD_ELEMENTS - else { - size_t commit_size; - if ((ret = get_txout_commitments_size(output, &commit_size)) != WALLY_OK) - goto error; - outputs_size += commit_size; - - if (sh_rangeproof) { - rangeproof_size += varbuff_get_length(output->rangeproof_len) + - varbuff_get_length(output->surjectionproof_len); - } - } -#else - else - return WALLY_EINVAL; -#endif - outputs_size += varbuff_get_length(output->script_len); + size_t wit_size = 0, *wit_p = sh_rangeproof ? &wit_size : NULL; + size_t n = txout_get_serialized_len(output, is_elements, wit_p); + if (!n) + goto error; /* Error getting txout length */ + outputs_size += n; + rangeproof_size += wit_size; } } @@ -2183,7 +2113,7 @@ static inline int tx_to_bip143_bytes(const struct wally_tx *tx, issuances_size += 1; } } -#endif +#endif /* BUILD_ELEMENTS */ if (inputs_size > buff_len || outputs_size > buff_len || rangeproof_size > buff_len || issuances_size > buff_len) { @@ -2225,7 +2155,7 @@ static inline int tx_to_bip143_bytes(const struct wally_tx *tx, #ifdef BUILD_ELEMENTS if (is_elements) { - /* Issuance */ + /* sha_issuances */ if (sh_anyonecanpay) memset(p, 0, SHA256_LEN); else { @@ -2345,234 +2275,6 @@ static inline int tx_to_bip143_bytes(const struct wally_tx *tx, return ret; } -static bool tr_is_input_hash_type(uint32_t sighash, uint32_t hash_type) -{ - return (sighash & WALLY_SIGHASH_TR_IN_MASK) == hash_type; -} - -static inline int tx_to_bip341_bytes(const struct wally_tx *tx, - const struct tx_serialize_opts *opts, - uint32_t flags, - unsigned char *bytes_out, size_t len, - size_t *written) -{ - unsigned char buff[TX_STACK_SIZE / 2], *buff_p = buff; - size_t i, is_elements, sub_size, buff_len = sizeof(buff); - const bool has_annex = opts->annex != NULL; - const bool sh_none = (opts->sighash & WALLY_SIGHASH_MASK) == WALLY_SIGHASH_NONE; - const bool sh_single = (opts->sighash & WALLY_SIGHASH_MASK) == WALLY_SIGHASH_SINGLE; - const bool sh_anyonecanpay = tr_is_input_hash_type(opts->sighash, WALLY_SIGHASH_ANYONECANPAY); - const bool sh_anyprevout = tr_is_input_hash_type(opts->sighash, WALLY_SIGHASH_ANYPREVOUT); - const bool sh_anyprevout_anyscript = tr_is_input_hash_type(opts->sighash, WALLY_SIGHASH_ANYPREVOUTANYSCRIPT); - unsigned char *p = bytes_out, *tmp_p; - int ret = WALLY_OK; - - /* Note we assume tx_to_bytes has already validated all inputs */ - (void)flags; - (void)len; - (void)is_elements; - -#ifdef BUILD_ELEMENTS - if ((ret = wally_tx_is_elements(tx, &is_elements)) != WALLY_OK || is_elements) - return WALLY_EINVAL; -#endif - - if (opts->key_version > 0x1) - return WALLY_EINVAL; /* Non-BIP341/342/118 key version */ - - switch (opts->sighash) { - case WALLY_SIGHASH_DEFAULT: - case WALLY_SIGHASH_ALL: - case WALLY_SIGHASH_NONE: - case WALLY_SIGHASH_SINGLE: - case WALLY_SIGHASH_ALL | WALLY_SIGHASH_ANYONECANPAY: - case WALLY_SIGHASH_NONE | WALLY_SIGHASH_ANYONECANPAY: - case WALLY_SIGHASH_SINGLE | WALLY_SIGHASH_ANYONECANPAY: - break; /* Always valid */ - case WALLY_SIGHASH_ALL | WALLY_SIGHASH_ANYPREVOUT: - case WALLY_SIGHASH_NONE | WALLY_SIGHASH_ANYPREVOUT: - case WALLY_SIGHASH_SINGLE | WALLY_SIGHASH_ANYPREVOUT: - case WALLY_SIGHASH_ALL | WALLY_SIGHASH_ANYPREVOUT | WALLY_SIGHASH_ANYONECANPAY: - case WALLY_SIGHASH_NONE | WALLY_SIGHASH_ANYPREVOUT | WALLY_SIGHASH_ANYONECANPAY: - case WALLY_SIGHASH_SINGLE | WALLY_SIGHASH_ANYPREVOUT | WALLY_SIGHASH_ANYONECANPAY: - if (opts->key_version != 1) - return WALLY_EINVAL; /* Only valid for key_version 1 */ - break; - default: - return WALLY_EINVAL; /* Unknown sighash type */ - } - - if (sh_single && opts->index >= tx->num_outputs) - return WALLY_EINVAL; /* no corresponding output for SIGHASH_SINGLE */ - - p += uint8_to_le_bytes(0, p); /* sighash epoch: 0x00 */ - p += uint8_to_le_bytes(opts->sighash, p); /* hash_type (1) */ - p += uint32_to_le_bytes(tx->version, p); /* nVersion (4) */ - p += uint32_to_le_bytes(tx->locktime, p); /* nLockTime (4) */ - - /* Compute the sizes needed for prevouts/scripts/output sub-hashes */ - sub_size = tx->num_inputs * (WALLY_TXHASH_LEN + sizeof(uint32_t)); - buff_len = sub_size > buff_len ? sub_size : buff_len; - - sub_size = 0; - for (i = 0; i < tx->num_inputs; ++i) { - const struct wally_map_item *script = wally_map_get_integer(opts->scripts, i); - if (!script || !script->value || !script->value_len) { - ret = WALLY_EINVAL; /* Missing script */ - goto error; - } - sub_size += varbuff_get_length(script->value_len); - } - buff_len = sub_size > buff_len ? sub_size : buff_len; - - if (sh_none) - sub_size = 0; - else if (sh_single) { - sub_size = sizeof(uint64_t); - sub_size += varbuff_get_length(tx->outputs[opts->index].script_len); - } else { - sub_size = 0; - for (i = 0; i < tx->num_outputs; ++i) { - sub_size += sizeof(uint64_t); - sub_size += varbuff_get_length(tx->outputs[i].script_len); - } - } - buff_len = sub_size > buff_len ? sub_size : buff_len; - - buff_len = opts->tapleaf_script_len > buff_len ? opts->tapleaf_script_len : buff_len; - - /* Allocate a larger buffer if needed to hold our sub-hashes data */ - if (buff_len > sizeof(buff) && !(buff_p = wally_malloc(buff_len))) - return WALLY_ENOMEM; - - if (!sh_anyonecanpay && !sh_anyprevout) { - /* sha_prevouts */ - tmp_p = buff_p; - for (i = 0; i < tx->num_inputs; ++i) { - memcpy(tmp_p, tx->inputs[i].txhash, WALLY_TXHASH_LEN); - uint32_to_le_bytes(tx->inputs[i].index, tmp_p + WALLY_TXHASH_LEN); - tmp_p += WALLY_TXHASH_LEN + sizeof(uint32_t); - } - if ((ret = wally_sha256(buff_p, tmp_p - buff_p, p, SHA256_LEN)) != WALLY_OK) - goto error; - p += SHA256_LEN; - - /* sha_amounts */ - tmp_p = buff_p; - for (i = 0; i < tx->num_inputs; ++i) - tmp_p += uint64_to_le_bytes(opts->prev_satoshis[i], tmp_p); - if ((ret = wally_sha256(buff_p, tmp_p - buff_p, p, SHA256_LEN)) != WALLY_OK) - goto error; - p += SHA256_LEN; - - /* sha_scriptpubkeys */ - tmp_p = buff_p; - for (i = 0; i < tx->num_inputs; ++i) { - const struct wally_map_item *script = wally_map_get_integer(opts->scripts, i); - tmp_p += varbuff_to_bytes(script->value, script->value_len, tmp_p); - } - if ((ret = wally_sha256(buff_p, tmp_p - buff_p, p, SHA256_LEN)) != WALLY_OK) - goto error; - p += SHA256_LEN; - - /* sha_sequences */ - tmp_p = buff_p; - for (i = 0; i < tx->num_inputs; ++i) - tmp_p += uint32_to_le_bytes(tx->inputs[i].sequence, tmp_p); - - if ((ret = wally_sha256(buff_p, tmp_p - buff_p, p, SHA256_LEN)) != WALLY_OK) - goto error; - p += SHA256_LEN; - } - - if (!sh_none && !sh_single) { - /* sha_outputs */ - tmp_p = buff_p; - for (i = 0; i < tx->num_outputs; ++i) { - tmp_p += uint64_to_le_bytes(tx->outputs[i].satoshi, tmp_p); - tmp_p += varbuff_to_bytes(tx->outputs[i].script, - tx->outputs[i].script_len, tmp_p); - } - if ((ret = wally_sha256(buff_p, tmp_p - buff_p, p, SHA256_LEN)) != WALLY_OK) - goto error; - p += SHA256_LEN; - } - - /* Data about this input: */ - p += uint8_to_le_bytes(opts->ext_flag * 2 + has_annex, p); /* spend_type (1) */ - - if (sh_anyonecanpay || sh_anyprevout) { - const struct wally_map_item *script = wally_map_get_integer(opts->scripts, opts->index); - if (!script || !script->value || script->value_len != WALLY_SCRIPTPUBKEY_P2TR_LEN || - script->value[0] != OP_1 || script->value[1] != 32u) { - ret = WALLY_EINVAL; /* Not a v1 segwit taproot script */ - goto error; - } - if (sh_anyonecanpay) { - /* outpoint (36) */ - memcpy(p, tx->inputs[opts->index].txhash, WALLY_TXHASH_LEN); - p += WALLY_TXHASH_LEN; - p += uint32_to_le_bytes(tx->inputs[opts->index].index, p); - } - p += uint64_to_le_bytes(opts->satoshi, p); /* amount (8) */ - p += varbuff_to_bytes(script->value, script->value_len, p); /* scriptPubKey (35) */ - p += uint32_to_le_bytes(tx->inputs[opts->index].sequence, p); /* nSequence (4) */ - } else if (sh_anyprevout_anyscript) { - p += uint32_to_le_bytes(tx->inputs[opts->index].sequence, p); /* nSequence (4) */ - } else { - p += uint32_to_le_bytes(opts->index, p); /* input_index (4) */ - } - - if (has_annex) { - /* sha_annex */ - tmp_p = buff_p + varbuff_to_bytes(opts->annex, opts->annex_len, buff_p); - if ((ret = wally_sha256(buff_p, tmp_p - buff_p, p, SHA256_LEN)) != WALLY_OK) - goto error; - p += SHA256_LEN; - } - - /* Data about this output: */ - if (sh_single) { - /* sha_single_output */ - const struct wally_tx_output *txout = &tx->outputs[opts->index]; - tmp_p = buff_p + uint64_to_le_bytes(txout->satoshi, buff_p); - tmp_p += varbuff_to_bytes(txout->script, txout->script_len, tmp_p); - if ((ret = wally_sha256(buff_p, tmp_p - buff_p, p, SHA256_LEN)) != WALLY_OK) - goto error; - p += SHA256_LEN; - } - - /* Tapscript Extensions: */ - if (opts->ext_flag == EXT_FLAG_BIP342) { - if (!sh_anyprevout_anyscript) { - /* tapleaf_hash (32): hash_TapLeaf(v || compact_size(size of s) || s) */ - buff_p[0] = 0xC0; /* leaf_version */ - tmp_p = buff_p + 1; - tmp_p += varbuff_to_bytes(opts->tapleaf_script, opts->tapleaf_script_len, tmp_p); - ret = wally_bip340_tagged_hash(buff_p, tmp_p - buff_p, - TAPLEAF(is_elements), p, SHA256_LEN); - if (ret != WALLY_OK) - goto error; - p += SHA256_LEN; - } - - p += uint8_to_le_bytes(opts->key_version & 0xff, p); /* key_version (1) */ - p += uint32_to_le_bytes(opts->codesep_position, p); /* codesep_pos (4) */ - } else if (opts->ext_flag) { - ret = WALLY_ERROR; /* Unknown extension flag */ - goto error; - } - - *written = p - bytes_out; - -error: - if (buff_p != buff) - clear_and_free(buff_p, buff_len); - else - wally_clear(buff, sizeof(buff)); - return ret; -} - static int tx_to_bytes(const struct wally_tx *tx, const struct tx_serialize_opts *opts, uint32_t flags, @@ -2625,9 +2327,6 @@ static int tx_to_bytes(const struct wally_tx *tx, if (opts && opts->bip143) return tx_to_bip143_bytes(tx, opts, flags, bytes_out, len, written); - if (opts && opts->bip341) - return tx_to_bip341_bytes(tx, opts, flags, bytes_out, len, written); - if (flags & WALLY_TX_FLAG_USE_WITNESS) { if (wally_tx_get_witness_count(tx, &witness_count) != WALLY_OK) return WALLY_EINVAL; @@ -3252,32 +2951,35 @@ int wally_tx_from_hex(const char *hex, uint32_t flags, int wally_tx_is_elements(const struct wally_tx *tx, size_t *written) { + if (written) + *written = 0; if (!tx || !written) return WALLY_EINVAL; *written = is_valid_elements_tx(tx); - return WALLY_OK; } int wally_tx_elements_input_is_pegin(const struct wally_tx_input *input, size_t *written) { + if (written) + *written = 0; if (!input || !written) return WALLY_EINVAL; *written = is_valid_elements_tx_input_pegin(input); - return WALLY_OK; } int wally_tx_is_coinbase(const struct wally_tx *tx, size_t *written) { + if (written) + *written = 0; if (!tx || !written) return WALLY_EINVAL; - *written = tx->num_inputs == 1 && is_valid_coinbase_input(tx->inputs); - + *written = tx->num_inputs == 1 && is_coinbase_input(tx->inputs); return WALLY_OK; } @@ -3291,16 +2993,14 @@ static int tx_get_signature_hash(const struct wally_tx *tx, uint32_t sighash, uint32_t tx_sighash, uint32_t flags, unsigned char *bytes_out, size_t len) { - unsigned char buff[TX_STACK_SIZE], *buff_p = buff, ext_flag = 0; + unsigned char buff[TX_STACK_SIZE], *buff_p = buff; size_t n, n2; size_t is_elements = 0; const bool is_bip143 = (flags & WALLY_TX_FLAG_USE_WITNESS) ? true : false; - const bool is_bip341 = false; int ret; const struct tx_serialize_opts opts = { sighash, tx_sighash, index, script, script_len, satoshi, - is_bip143, value, value_len, is_bip341, ext_flag, NULL, 0, NULL, - NULL, 0, 0, 0, NULL, 0 + is_bip143, value, value_len }; if (!is_valid_tx(tx) || BYTES_INVALID(script, script_len) || @@ -3382,47 +3082,30 @@ int wally_tx_get_btc_taproot_signature_hash( uint32_t sighash, uint32_t flags, unsigned char *bytes_out, size_t len) { - unsigned char buff[256]; /* Largest possible hash preimage is 244 bytes */ - const bool is_bip143 = false, is_bip341 = true; - const struct tx_serialize_opts opts = { - sighash, sighash, index, NULL, 0, - values && index < num_values ? values[index] : 0, - is_bip143, NULL, 0, is_bip341, tapleaf_script ? EXT_FLAG_BIP342 : 0, - values, num_values, scripts, tapleaf_script, tapleaf_script_len, - key_version, codesep_position, annex, annex_len - }; - size_t is_elements, n, n2; + struct wally_map values_map; int ret; - (void)is_elements; - - if (!values || !num_values || index >= num_values || - BYTES_INVALID(tapleaf_script, tapleaf_script_len) || - BYTES_INVALID(annex, annex_len) || (annex && *annex != 0x50) || - flags || !bytes_out || len != SHA256_LEN) - return WALLY_EINVAL; - -#ifdef BUILD_ELEMENTS - if (wally_tx_is_elements(tx, &is_elements) != WALLY_OK || is_elements) + if (flags) return WALLY_EINVAL; -#endif - - if ((ret = tx_get_length(tx, &opts, 0, &n, false)) != WALLY_OK) + ret = wally_map_init(num_values, NULL, &values_map); + if (ret != WALLY_OK) return ret; - - if (n > sizeof(buff)) - return WALLY_ERROR; /* Will never happen unless buff size is reduced */ - - ret = tx_to_bytes(tx, &opts, 0, buff, sizeof(buff), &n2, false); - if (ret == WALLY_OK) { - if (n != n2) - ret = WALLY_ERROR; /* tx_get_length/tx_to_bytes mismatch, should not happen! */ - else - ret = wally_bip340_tagged_hash(buff, n, TAPSIGHASH(false), bytes_out, len); - } - wally_clear(buff, n); + /* Convert values array into a non-owning map of input values */ + for (size_t i = 0; i < num_values; ++i) { + values_map.items[i].key_len = i; + values_map.items[i].value = (unsigned char*)(values + i); + values_map.items[i].value_len = sizeof(values[0]); + } + values_map.num_items = num_values; + ret = wally_tx_get_input_signature_hash(tx, index, scripts, NULL, &values_map, + tapleaf_script, tapleaf_script_len, + key_version, codesep_position, + annex, annex_len, NULL, 0, sighash, + WALLY_SIGTYPE_SW_V1, NULL, bytes_out, len); + wally_free(values_map.items); /* No need to clear the value pointers */ return ret; } +#ifndef WALLY_ABI_NO_ELEMENTS int wally_tx_get_elements_signature_hash(const struct wally_tx *tx, size_t index, const unsigned char *script, size_t script_len, @@ -3542,6 +3225,7 @@ int wally_tx_elements_issuance_calculate_reissuance_token(const unsigned char *e buff, sizeof(buff), bytes_out, len); } +#endif /* WALLY_ABI_NO_ELEMENTS */ int wally_tx_get_total_output_satoshi(const struct wally_tx *tx, uint64_t *value_out) { diff --git a/src/tx_io.c b/src/tx_io.c new file mode 100644 index 000000000..3e2404cd1 --- /dev/null +++ b/src/tx_io.c @@ -0,0 +1,737 @@ +#include "internal.h" +#include +#include "pullpush.h" +#include "script.h" +#include "script_int.h" +#include "tx_io.h" +#include + +#define SIGTYPE_ALL (WALLY_SIGTYPE_PRE_SW | WALLY_SIGTYPE_SW_V0 | WALLY_SIGTYPE_SW_V1) + +/* Cache keys for data that is constant while signing a given tx. + * We also cache other data keyed by their binary value directly. + */ +#define TXIO_UNCACHED 0 /* Signals that data should not be cached */ +#define TXIO_SHA_TAPSIGHASH_CTX 1 /* Initial sha256_ctx for taproot bip340 hashing */ +#define TXIO_SHA_OUTPOINT_FLAGS 2 /* Taproot cached data ... */ +#define TXIO_SHA_PREVOUTS 3 +#define TXIO_SHA_AMOUNTS 4 +#define TXIO_SHA_ASSET_AMOUNTS 5 +#define TXIO_SHA_SCRIPTPUBKEYS 6 +#define TXIO_SHA_SEQUENCES 7 +#define TXIO_SHA_ISSUANCES 8 +#define TXIO_SHA_ISSUANCE_RANGEPROOFS 9 +#define TXIO_SHA_OUTPUTS 10 +#define TXIO_SHA_OUTPUT_WITNESSES 11 /* ... end of taproot cached data */ + +/* SHA256(TapSighash) */ +static const unsigned char TAPSIGHASH_SHA256[SHA256_LEN] = { + 0xf4, 0x0a, 0x48, 0xdf, 0x4b, 0x2a, 0x70, 0xc8, 0xb4, 0x92, 0x4b, 0xf2, 0x65, 0x46, 0x61, 0xed, + 0x3d, 0x95, 0xfd, 0x66, 0xa3, 0x13, 0xeb, 0x87, 0x23, 0x75, 0x97, 0xc6, 0x28, 0xe4, 0xa0, 0x31 +}; +/* SHA256(TapLeaf) */ +static const unsigned char TAPLEAF_SHA256[SHA256_LEN] = { + 0xae, 0xea, 0x8f, 0xdc, 0x42, 0x08, 0x98, 0x31, 0x05, 0x73, 0x4b, 0x58, 0x08, 0x1d, 0x1e, 0x26, + 0x38, 0xd3, 0x5f, 0x1c, 0xb5, 0x40, 0x08, 0xd4, 0xd3, 0x57, 0xca, 0x03, 0xbe, 0x78, 0xe9, 0xee +}; + +#ifdef BUILD_ELEMENTS +/* SHA256(TapSighash/elements) */ +static const unsigned char TAPSIGHASH_SHA256_ELEMENTS[SHA256_LEN] = { + 0xe3, 0x43, 0x16, 0x49, 0xdc, 0xb6, 0x48, 0x53, 0x3d, 0x8e, 0x36, 0x4a, 0xff, 0xd6, 0x06, 0xcb, + 0x7d, 0xe9, 0x78, 0xd6, 0x0c, 0xd0, 0x12, 0x2d, 0x1e, 0x55, 0x17, 0x48, 0x75, 0xca, 0xba, 0x08 +}; +/* SHA256(TapLeaf/elements) */ +static const unsigned char TAPLEAF_SHA256_ELEMENTS[SHA256_LEN] = { + 0x69, 0xff, 0xb5, 0x5a, 0xb8, 0xc8, 0x1c, 0x21, 0xf5, 0x8b, 0x2a, 0xdc, 0xb0, 0x83, 0x5a, 0x08, + 0x60, 0x8a, 0xf5, 0x9d, 0x04, 0x2f, 0x03, 0x37, 0x64, 0x33, 0x9c, 0xd8, 0xe6, 0xba, 0x33, 0xe7 +}; +#define TAPSIGHASH_SHA256(is_elements) (is_elements ? TAPSIGHASH_SHA256_ELEMENTS : TAPSIGHASH_SHA256) +#define TAPLEAF_SHA256(is_elements) (is_elements ? TAPLEAF_SHA256_ELEMENTS : TAPLEAF_SHA256) +#else +#define TAPSIGHASH_SHA256(is_elements) TAPSIGHASH_SHA256 +#define TAPLEAF_SHA256(is_elements) TAPLEAF_SHA256 +#endif /* BUILD_ELEMENTS */ + +static bool script_len_ok(size_t len) { return len != 0; } +static bool asset_len_ok(size_t len) { return len == WALLY_TX_ASSET_CT_ASSET_LEN; } +static bool satoshi_len_ok(size_t len) { return len == sizeof(uint64_t); } +static bool value_len_ok(size_t len) +{ + return len == WALLY_TX_ASSET_CT_VALUE_LEN || + len == WALLY_TX_ASSET_CT_VALUE_UNBLIND_LEN; +} + +/* Ensure 'm' is integer-indexed with num_items valid items */ +static bool map_has_all(const struct wally_map *m, size_t num_items, + bool (*len_fn)(size_t)) +{ + if (!m || m->num_items != num_items) + return false; + for (size_t i = 0; i < num_items; ++i) { + const struct wally_map_item *item = m->items + i; + if (item->key || item->key_len != i || + !item->value || !len_fn(item->value_len)) + return false; + } + return true; +} + +/* Ensure 'm' is integer-indexed containing a valid item for 'index' */ +static bool map_has_one(const struct wally_map *m, size_t index, + bool (*len_fn)(size_t)) +{ + if (!m || !m->num_items) + return false; + for (size_t i = 0; i < m->num_items; ++i) { + const struct wally_map_item *item = m->items + i; + if (item->key || !item->value || !len_fn(item->value_len)) + return false; + if (index == item->key_len) + return true; + } + return false; +} + +static inline void hash_u8(struct sha256_ctx *ctx, uint8_t v) +{ + sha256_u8(ctx, v); +} + +static inline void hash_le32(struct sha256_ctx *ctx, uint32_t v) +{ + sha256_le32(ctx, v); +} + +static inline void hash_le64(struct sha256_ctx *ctx, uint64_t v) +{ + sha256_le64(ctx, v); +} + +static void hash_map_le64(struct sha256_ctx *ctx, + const struct wally_map *m, size_t index) +{ + const struct wally_map_item *item = wally_map_get_integer(m, index); + uint64_t v; + memcpy(&v, item->value, item->value_len); + hash_le64(ctx, v); +} + +static inline void hash_bytes(struct sha256_ctx *ctx, + const unsigned char *bytes, size_t bytes_len) +{ + sha256_update(ctx, bytes, bytes_len); +} + +static void hash_varbuff(struct sha256_ctx *ctx, + const unsigned char *bytes, size_t bytes_len) +{ + unsigned char varbuff_len[9]; + size_t n = varint_to_bytes(bytes_len, varbuff_len); + sha256_update(ctx, varbuff_len, n); + sha256_update(ctx, bytes, bytes_len); +} + +static void hash_map_varbuff(struct sha256_ctx *ctx, + const struct wally_map *m, size_t index) +{ + const struct wally_map_item *item = wally_map_get_integer(m, index); + hash_varbuff(ctx, item->value, item->value_len); +} + +static bool txio_hash_cached_item(cursor_io *io, uint32_t key) +{ + const struct wally_map_item *item; + item = io->cache ? wally_map_get_integer(io->cache, key) : NULL; + if (!item) + return false; + hash_bytes(&io->ctx, item->value, item->value_len); + return true; +} + +static void txio_hash_sha256_ctx(cursor_io *io, struct sha256_ctx *ctx, int key) +{ + struct sha256 hash; + sha256_done(ctx, &hash); + hash_bytes(&io->ctx, hash.u.u8, sizeof(hash)); + if (io->cache && key != TXIO_UNCACHED) + wally_map_add_integer(io->cache, key, hash.u.u8, sizeof(hash)); +} + +static void txio_done(cursor_io *io) +{ + struct sha256 hash; + sha256_done(&io->ctx, &hash); + push_bytes(&io->cursor, &io->max, hash.u.u8, sizeof(hash)); +} + +/* Initialize a sha256 context for bip340 tagged hashing. + * 'hash' must be SHA256(tag), e.g. 'TapSighash', 'TapLeaf' etc. + */ +static void tagged_hash_init(struct sha256_ctx *ctx, + const unsigned char *hash, size_t hash_len) +{ + sha256_init(ctx); + hash_bytes(ctx, hash, hash_len); + hash_bytes(ctx, hash, hash_len); +} + +#ifdef BUILD_ELEMENTS +static void hash_commmitment(struct sha256_ctx *ctx, + const unsigned char *bytes, size_t bytes_len) +{ + if (!bytes_len) + hash_u8(ctx, 0); + else + hash_bytes(ctx, bytes, bytes_len); +} + +static void hash_map_commmitment(struct sha256_ctx *ctx, + const struct wally_map *m, size_t index) +{ + const struct wally_map_item *item = wally_map_get_integer(m, index); + hash_commmitment(ctx, item->value, item->value_len); +} + +static void hash_issuance_rangeproofs(struct sha256_ctx *ctx, + const struct wally_tx_input *txin) +{ + if (!(txin->features & WALLY_TX_IS_ISSUANCE)) { + hash_u8(ctx, 0); + hash_u8(ctx, 0); + return; + } + hash_varbuff(ctx, txin->issuance_amount_rangeproof, txin->issuance_amount_rangeproof_len); + hash_varbuff(ctx, txin->inflation_keys_rangeproof, txin->inflation_keys_rangeproof_len); +} + +static void hash_output_elements(struct sha256_ctx *ctx, + const struct wally_tx_output *txout) +{ + hash_commmitment(ctx, txout->asset, txout->asset_len); + hash_commmitment(ctx, txout->value, txout->value_len); + hash_commmitment(ctx, txout->nonce, txout->nonce_len); + hash_varbuff(ctx, txout->script, txout->script_len); +} + +static void hash_output_witness(struct sha256_ctx *ctx, + const struct wally_tx_output *txout) +{ + hash_varbuff(ctx, txout->surjectionproof, txout->surjectionproof_len); + hash_varbuff(ctx, txout->rangeproof, txout->rangeproof_len); +} + +static void txio_hash_sha_outpoint_flags(cursor_io *io, const struct wally_tx *tx) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_OUTPOINT_FLAGS)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < tx->num_inputs; ++i) { + const struct wally_tx_input *txin = tx->inputs + i; + uint8_t v = 0; + if (txin->features & WALLY_TX_IS_ISSUANCE) + v = WALLY_TX_ISSUANCE_FLAG >> 24; + else if (txin->features & WALLY_TX_IS_PEGIN) + v = WALLY_TX_PEGIN_FLAG >> 24; + hash_u8(&ctx, v); + } + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_OUTPOINT_FLAGS); + } +} + +static void txio_hash_sha_asset_amounts(cursor_io *io, + const struct wally_map *values, + const struct wally_map *assets) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_ASSET_AMOUNTS)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < values->num_items; ++i) { + hash_commmitment(&ctx, assets->items[i].value, assets->items[i].value_len); + hash_commmitment(&ctx, values->items[i].value, values->items[i].value_len); + } + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_ASSET_AMOUNTS); + } +} + +static void txio_hash_sha_issuances(cursor_io *io, const struct wally_tx *tx) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_ISSUANCES)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < tx->num_inputs; ++i) { + const struct wally_tx_input *txin = tx->inputs + i; + if (!(txin->features & WALLY_TX_IS_ISSUANCE)) { + hash_u8(&ctx, 0); + continue; + } + hash_bytes(&ctx, txin->blinding_nonce, sizeof(txin->blinding_nonce)); + hash_bytes(&ctx, txin->entropy, sizeof(txin->entropy)); + hash_commmitment(&ctx, txin->issuance_amount, txin->issuance_amount_len); + hash_commmitment(&ctx, txin->inflation_keys, txin->inflation_keys_len); + } + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_ISSUANCES); + } +} + +static void txio_hash_sha_issuance_rangeproofs(cursor_io *io, const struct wally_tx *tx) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_ISSUANCE_RANGEPROOFS)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < tx->num_inputs; ++i) + hash_issuance_rangeproofs(&ctx, tx->inputs + i); + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_ISSUANCE_RANGEPROOFS); + } +} + +static void txio_hash_sha_outputs_elements(cursor_io *io, const struct wally_tx *tx) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_OUTPUTS)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < tx->num_outputs; ++i) + hash_output_elements(&ctx, tx->outputs + i); + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_OUTPUTS); + } +} + +static void txio_hash_sha_output_witnesses(cursor_io *io, const struct wally_tx *tx) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_OUTPUT_WITNESSES)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < tx->num_outputs; ++i) + hash_output_witness(&ctx, tx->outputs + i); + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_OUTPUT_WITNESSES); + } +} + +static void txio_hash_outpoint_flag(cursor_io *io, const struct wally_tx_input *txin) +{ + unsigned char outpoint_flag = 0; + if (txin->features & WALLY_TX_IS_ISSUANCE) + outpoint_flag |= WALLY_TX_ISSUANCE_FLAG >> 24; + if (txin->features & WALLY_TX_IS_PEGIN) + outpoint_flag |= WALLY_TX_PEGIN_FLAG >> 24; + hash_u8(&io->ctx, outpoint_flag); +} + +static void txio_hash_input_elements(cursor_io *io, + const struct wally_tx *tx, size_t index, + const struct wally_map *scripts, + const struct wally_map *assets, + const struct wally_map *values) +{ + const struct wally_tx_input *txin = tx->inputs + index; + + hash_map_commmitment(&io->ctx, assets, index); + hash_map_commmitment(&io->ctx, values, index); + hash_map_varbuff(&io->ctx, scripts, index); + hash_le32(&io->ctx, txin->sequence); + + if (!(txin->features & WALLY_TX_IS_ISSUANCE)) + hash_u8(&io->ctx, 0); + else { + /* asset_issuance */ + hash_bytes(&io->ctx, txin->blinding_nonce, sizeof(txin->blinding_nonce)); + hash_bytes(&io->ctx, txin->entropy, sizeof(txin->entropy)); + hash_commmitment(&io->ctx, txin->issuance_amount, txin->issuance_amount_len); + hash_commmitment(&io->ctx, txin->inflation_keys, txin->inflation_keys_len); + { + /* sha_single_issuance_rangeproofs */ + struct sha256_ctx ctx; + sha256_init(&ctx); + hash_issuance_rangeproofs(&ctx, txin); + txio_hash_sha256_ctx(io, &ctx, TXIO_UNCACHED); + } + } +} + +static void txio_hash_sha_single_output_elements(cursor_io *io, + const struct wally_tx_output *txout) +{ + struct sha256_ctx ctx; + sha256_init(&ctx); + hash_output_elements(&ctx, txout); + txio_hash_sha256_ctx(io, &ctx, TXIO_UNCACHED); +} + +static void txio_hash_sha_single_output_witness(cursor_io *io, + const struct wally_tx_output *txout) +{ + struct sha256_ctx ctx; + sha256_init(&ctx); + hash_output_witness(&ctx, txout); + txio_hash_sha256_ctx(io, &ctx, TXIO_UNCACHED); +} +#endif /* BUILD_ELEMENTS */ + +static void txio_hash_sha_prevouts(cursor_io *io, const struct wally_tx *tx) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_PREVOUTS)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < tx->num_inputs; ++i) { + hash_bytes(&ctx, tx->inputs[i].txhash, WALLY_TXHASH_LEN); + hash_le32(&ctx, tx->inputs[i].index); + } + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_PREVOUTS); + } +} + +static void txio_hash_sha_amounts(cursor_io *io, const struct wally_map *values) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_AMOUNTS)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < values->num_items; ++i) { + uint64_t v; + memcpy(&v, values->items[i].value, values->items[i].value_len); + hash_le64(&ctx, v); + } + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_AMOUNTS); + } +} + +static void txio_hash_sha_scriptpubkeys(cursor_io *io, const struct wally_map *scripts) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_SCRIPTPUBKEYS)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < scripts->num_items; ++i) + hash_varbuff(&ctx, scripts->items[i].value, scripts->items[i].value_len); + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_SCRIPTPUBKEYS); + } +} + +static void txio_hash_sha_sequences(cursor_io *io, const struct wally_tx *tx) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_SEQUENCES)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < tx->num_inputs; ++i) + hash_le32(&ctx, tx->inputs[i].sequence); + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_SEQUENCES); + } +} + +static void txio_hash_sha_outputs(cursor_io *io, const struct wally_tx *tx) +{ + if (!txio_hash_cached_item(io, TXIO_SHA_OUTPUTS)) { + struct sha256_ctx ctx; + sha256_init(&ctx); + for (size_t i = 0; i < tx->num_outputs; ++i) { + hash_le64(&ctx, tx->outputs[i].satoshi); + hash_varbuff(&ctx, tx->outputs[i].script, tx->outputs[i].script_len); + } + txio_hash_sha256_ctx(io, &ctx, TXIO_SHA_OUTPUTS); + } +} + +static void txio_hash_outpoint(cursor_io *io, const struct wally_tx_input *txin) +{ + hash_bytes(&io->ctx, txin->txhash, sizeof(txin->txhash)); + hash_le32(&io->ctx, txin->index); +} + +static void txio_hash_input(cursor_io *io, + const struct wally_tx *tx, size_t index, + const struct wally_map *scripts, + const struct wally_map *values) +{ + hash_map_le64(&io->ctx, values, index); + hash_map_varbuff(&io->ctx, scripts, index); + hash_le32(&io->ctx, tx->inputs[index].sequence); +} + +static void txio_hash_sha_single_output(cursor_io *io, const struct wally_tx_output *txout) +{ + struct sha256_ctx ctx; + sha256_init(&ctx); + hash_le64(&ctx, txout->satoshi); + hash_varbuff(&ctx, txout->script, txout->script_len); + txio_hash_sha256_ctx(io, &ctx, TXIO_UNCACHED); +} + +static void txio_hash_annex(cursor_io *io, + const unsigned char *annex, size_t annex_len) +{ + const struct wally_map_item *item; + item = io->cache ? wally_map_get(io->cache, annex, annex_len) : NULL; + if (item) + hash_bytes(&io->ctx, item->value, item->value_len); + else { + struct sha256_ctx ctx; + sha256_init(&ctx); + hash_varbuff(&ctx, annex, annex_len); + struct sha256 hash; + sha256_done(&ctx, &hash); + hash_bytes(&io->ctx, hash.u.u8, sizeof(hash)); + if (io->cache) + wally_map_add(io->cache, annex, annex_len, hash.u.u8, sizeof(hash)); + } +} + +static void txio_hash_tapleaf_hash(cursor_io *io, + const unsigned char *tapleaf_script, size_t tapleaf_script_len, + bool is_elements) +{ + const struct wally_map_item *item; + item = io->cache ? wally_map_get(io->cache, tapleaf_script, tapleaf_script_len) : NULL; + if (item) { + hash_bytes(&io->ctx, item->value, item->value_len); + } else { + struct sha256_ctx ctx; + struct sha256 hash; + tagged_hash_init(&ctx, TAPLEAF_SHA256(is_elements), SHA256_LEN); + hash_u8(&ctx, 0xc0); /* leaf_version */ + hash_varbuff(&ctx, tapleaf_script, tapleaf_script_len); + sha256_done(&ctx, &hash); + hash_bytes(&io->ctx, hash.u.u8, sizeof(hash)); + if (io->cache) + wally_map_add(io->cache, tapleaf_script, tapleaf_script_len, hash.u.u8, sizeof(hash)); + } +} + +/* BIP 341 */ +static void txio_bip341_init(cursor_io *io, + const unsigned char *genesis_blockhash, size_t genesis_blockhash_len) +{ + const struct wally_map_item *item; + item = io->cache ? wally_map_get_integer(io->cache, TXIO_SHA_TAPSIGHASH_CTX) : NULL; + if (item) { + /* Note we hash the intial sha256_ctx itself here and so memcpy it */ + memcpy(&io->ctx, item->value, item->value_len); + return; + } + + tagged_hash_init(&io->ctx, TAPSIGHASH_SHA256(genesis_blockhash != NULL), SHA256_LEN); + if (genesis_blockhash) { + hash_bytes(&io->ctx, genesis_blockhash, genesis_blockhash_len); + hash_bytes(&io->ctx, genesis_blockhash, genesis_blockhash_len); + } + if (io->cache) + wally_map_add_integer(io->cache, TXIO_SHA_TAPSIGHASH_CTX, + (const unsigned char*)&io->ctx, sizeof(io->ctx)); +} + +static inline uint32_t tr_get_output_sighash_type(uint32_t sighash) +{ + if (sighash == WALLY_SIGHASH_DEFAULT) + return WALLY_SIGHASH_ALL; + return sighash & 0x3; +} + +static inline bool bip341_is_input_hash_type(uint32_t sighash, uint32_t hash_type) +{ + return (sighash & WALLY_SIGHASH_TR_IN_MASK) == hash_type; +} + +static int bip341_signature_hash( + const struct wally_tx *tx, size_t index, + const struct wally_map *scripts, + const struct wally_map *assets, + const struct wally_map *values, + const unsigned char *tapleaf_script, size_t tapleaf_script_len, + uint32_t key_version, + uint32_t codesep_position, + const unsigned char *annex, size_t annex_len, + const unsigned char *genesis_blockhash, size_t genesis_blockhash_len, + uint32_t sighash, + struct wally_map *cache, + unsigned char *bytes_out, size_t len) +{ + const struct wally_tx_input *txin = tx ? tx->inputs + index : NULL; + const struct wally_tx_output *txout = tx ? tx->outputs + index : NULL; + size_t is_elements = 0; + const uint32_t output_type = tr_get_output_sighash_type(sighash); + const bool sh_anyonecanpay = sighash & WALLY_SIGHASH_ANYONECANPAY; + const bool sh_anyprevout = bip341_is_input_hash_type(sighash, WALLY_SIGHASH_ANYPREVOUT); + const bool sh_anyprevout_anyscript = bip341_is_input_hash_type(sighash, WALLY_SIGHASH_ANYPREVOUTANYSCRIPT); + cursor_io io; + int ret = WALLY_OK; + + if (!tx || index >= tx->num_inputs || + !values || + BYTES_INVALID(tapleaf_script, tapleaf_script_len) || + key_version > 1 || + codesep_position != WALLY_NO_CODESEPARATOR || /* TODO: Add support */ + BYTES_INVALID(annex, annex_len) || (annex && *annex != 0x50) || + BYTES_INVALID_N(genesis_blockhash, genesis_blockhash_len, SHA256_LEN) || + !bytes_out || len != SHA256_LEN) + return WALLY_EINVAL; + +#ifdef BUILD_ELEMENTS + if ((ret = wally_tx_is_elements(tx, &is_elements)) != WALLY_OK) + return ret; +#endif + if (is_elements) { + if (!genesis_blockhash) + return WALLY_EINVAL; + } else { + genesis_blockhash = NULL; + genesis_blockhash_len = 0; + } + + { + /* Validate input scripts/values/assets: + * For ACP/APO, we must have the items at 'index', and look them up. + * Otherwise we need all values, and iterate them. + */ + const struct wally_map_item *item; + bool (*value_len_fn)(size_t) = is_elements ? value_len_ok : satoshi_len_ok; + if (!sh_anyonecanpay && !sh_anyprevout) { + if (!map_has_all(scripts, tx->num_inputs, script_len_ok) || + !map_has_all(values, tx->num_inputs, value_len_fn) || + (is_elements && !map_has_all(assets, tx->num_inputs, asset_len_ok))) + return WALLY_EINVAL; + } else { + if (!map_has_one(scripts, index, script_len_ok) || + !map_has_one(values, index, value_len_fn) || + (is_elements && !map_has_one(assets, index, asset_len_ok))) + return WALLY_EINVAL; + } + item = wally_map_get_integer(scripts, index); + if (!scriptpubkey_is_p2tr(item->value, item->value_len)) + return WALLY_EINVAL; + } + + switch (sighash) { + case WALLY_SIGHASH_DEFAULT: + case WALLY_SIGHASH_ALL: + case WALLY_SIGHASH_NONE: + case WALLY_SIGHASH_SINGLE: + case WALLY_SIGHASH_ALL | WALLY_SIGHASH_ANYONECANPAY: + case WALLY_SIGHASH_NONE | WALLY_SIGHASH_ANYONECANPAY: + case WALLY_SIGHASH_SINGLE | WALLY_SIGHASH_ANYONECANPAY: + break; /* Always valid */ + case WALLY_SIGHASH_ALL | WALLY_SIGHASH_ANYPREVOUT: + case WALLY_SIGHASH_NONE | WALLY_SIGHASH_ANYPREVOUT: + case WALLY_SIGHASH_SINGLE | WALLY_SIGHASH_ANYPREVOUT: + case WALLY_SIGHASH_ALL | WALLY_SIGHASH_ANYPREVOUT | WALLY_SIGHASH_ANYONECANPAY: + case WALLY_SIGHASH_NONE | WALLY_SIGHASH_ANYPREVOUT | WALLY_SIGHASH_ANYONECANPAY: + case WALLY_SIGHASH_SINGLE | WALLY_SIGHASH_ANYPREVOUT | WALLY_SIGHASH_ANYONECANPAY: + if (key_version != 1) + return WALLY_EINVAL; /* Only valid for key_version 1 */ + if (is_elements) + return WALLY_ERROR; /* Elements: unsure of Activation status/no ELIP */ + break; + default: + return WALLY_EINVAL; /* Unknown sighash type */ + } + + /* Init */ + io.cache = cache; + io.cursor = bytes_out; + io.max = len; + txio_bip341_init(&io, genesis_blockhash, genesis_blockhash_len); + if (!is_elements) + hash_u8(&io.ctx, 0); /* sighash epoch */ + /* Tx data */ + hash_u8(&io.ctx, sighash); /* hash_type */ + hash_le32(&io.ctx, tx->version); + hash_le32(&io.ctx, tx->locktime); +#ifdef BUILD_ELEMENTS + if (is_elements & !sh_anyonecanpay) + txio_hash_sha_outpoint_flags(&io, tx); +#endif + if (!sh_anyonecanpay && !sh_anyprevout) { + txio_hash_sha_prevouts(&io, tx); +#ifdef BUILD_ELEMENTS + if (is_elements) + txio_hash_sha_asset_amounts(&io, values, assets); + else +#endif + txio_hash_sha_amounts(&io, values); + txio_hash_sha_scriptpubkeys(&io, scripts); + txio_hash_sha_sequences(&io, tx); +#ifdef BUILD_ELEMENTS + if (is_elements) { + txio_hash_sha_issuances(&io, tx); + txio_hash_sha_issuance_rangeproofs(&io, tx); + } +#endif + } + if (output_type == WALLY_SIGHASH_ALL) { +#ifdef BUILD_ELEMENTS + if (is_elements) { + txio_hash_sha_outputs_elements(&io, tx); + txio_hash_sha_output_witnesses(&io, tx); + } else +#endif + txio_hash_sha_outputs(&io, tx); + } + /* Input data */ + hash_u8(&io.ctx, (tapleaf_script ? 1 : 0) * 2 + (annex ? 1 : 0)); /* spend_type */ + if (sh_anyonecanpay || sh_anyprevout) { + if (sh_anyonecanpay) { +#ifdef BUILD_ELEMENTS + if (is_elements) + txio_hash_outpoint_flag(&io, txin); +#endif + txio_hash_outpoint(&io, txin); + } +#ifdef BUILD_ELEMENTS + if (is_elements) + txio_hash_input_elements(&io, tx, index, scripts, assets, values); + else +#endif + txio_hash_input(&io, tx, index, scripts, values); + } else if (sh_anyprevout_anyscript) { + hash_le32(&io.ctx, tx->inputs[index].sequence); /* nSequence */ + } else { + hash_le32(&io.ctx, index); /* input_index */ + } + if (annex) { + txio_hash_annex(&io, annex, annex_len); + } + /* Output data */ + if (output_type == WALLY_SIGHASH_SINGLE) { +#ifdef BUILD_ELEMENTS + if (is_elements) { + txio_hash_sha_single_output_elements(&io, txout); + txio_hash_sha_single_output_witness(&io, txout); + } else +#endif + txio_hash_sha_single_output(&io, txout); + } + /* Tapscript Extensions */ + if (tapleaf_script) { + if (!sh_anyprevout_anyscript) + txio_hash_tapleaf_hash(&io, tapleaf_script, tapleaf_script_len, is_elements); + hash_u8(&io.ctx, key_version & 0xff); + hash_le32(&io.ctx, codesep_position); + } + txio_done(&io); + if (io.max) + ret = WALLY_ERROR; /* Wrote the wrong number of bytes: should not happen! */ + return ret; +} + +int wally_tx_get_input_signature_hash( + const struct wally_tx *tx, size_t index, + const struct wally_map *scripts, + const struct wally_map *assets, + const struct wally_map *values, + const unsigned char *script, size_t script_len, + uint32_t key_version, + uint32_t codesep_position, + const unsigned char *annex, size_t annex_len, + const unsigned char *genesis_blockhash, size_t genesis_blockhash_len, + uint32_t sighash, + uint32_t flags, + struct wally_map *cache, + unsigned char *bytes_out, size_t len) +{ + if (!flags || (flags & ~SIGTYPE_ALL)) + return WALLY_EINVAL; + if (flags & WALLY_SIGTYPE_SW_V1) + return bip341_signature_hash(tx, index, scripts, assets, values, + script, script_len, + key_version, codesep_position, + annex, annex_len, + genesis_blockhash, genesis_blockhash_len, + sighash, cache, bytes_out, len); + return WALLY_ERROR; /* FIXME: Support segwit/pre-segwit hashing */ +} diff --git a/src/tx_io.h b/src/tx_io.h new file mode 100644 index 000000000..da888168c --- /dev/null +++ b/src/tx_io.h @@ -0,0 +1,19 @@ +#ifndef LIBWALLY_CORE_TX_IO_H +#define LIBWALLY_CORE_TX_IO_H 1 + +#include +#include "ccan/ccan/crypto/sha256/sha256.h" + +/* Suggested initial size of a signing cache to avoid re-allocations */ +#define TXIO_CACHE_INITIAL_SIZE 16 + +/* A cursor for pushing/pulling tx bytes for hashing */ +typedef struct cursor_io +{ + struct sha256_ctx ctx; + struct wally_map *cache; + unsigned char *cursor; + size_t max; +} cursor_io; + +#endif /* LIBWALLY_CORE_TX_IO_H */ diff --git a/src/wasm_package/src/const.js b/src/wasm_package/src/const.js index 9c48139a3..7b87a32ba 100755 --- a/src/wasm_package/src/const.js +++ b/src/wasm_package/src/const.js @@ -215,6 +215,10 @@ export const WALLY_SIGHASH_NONE = 0x02; export const WALLY_SIGHASH_RANGEPROOF = 0x40 ; /* Liquid/Elements only */ export const WALLY_SIGHASH_SINGLE = 0x03; export const WALLY_SIGHASH_TR_IN_MASK = 0xc0; /* Taproot mask for determining input hash type */ +export const WALLY_SIGTYPE_MASK = 0xf; /* Mask for signature hash in signature hash flags */ +export const WALLY_SIGTYPE_PRE_SW = 0x1; /* Pre-segwit signature hash */ +export const WALLY_SIGTYPE_SW_V0 = 0x2; /* Segwit v0 signature hash */ +export const WALLY_SIGTYPE_SW_V1 = 0x3; /* Segwit v1 (taproot) signature hash */ export const WALLY_TXHASH_LEN = 32; /** Size of a transaction hash in bytes */ export const WALLY_TX_ASSET_CT_ASSET_LEN = 33; /* version byte + 32 bytes */ export const WALLY_TX_ASSET_CT_ASSET_PREFIX_A = 0x0a; diff --git a/src/wasm_package/src/functions.js b/src/wasm_package/src/functions.js index fef08da9a..adf23cfad 100644 --- a/src/wasm_package/src/functions.js +++ b/src/wasm_package/src/functions.js @@ -602,6 +602,8 @@ export const psbt_set_version = wrap('wally_psbt_set_version', [T.OpaqueRef, T.I export const psbt_sign = wrap('wally_psbt_sign', [T.OpaqueRef, T.Bytes, T.Int32]); export const psbt_sign_bip32 = wrap('wally_psbt_sign_bip32', [T.OpaqueRef, T.OpaqueRef, T.Int32]); export const psbt_sign_input_bip32 = wrap('wally_psbt_sign_input_bip32', [T.OpaqueRef, T.Int32, T.Int32, T.Bytes, T.OpaqueRef, T.Int32]); +export const psbt_signing_cache_disable = wrap('wally_psbt_signing_cache_disable', [T.OpaqueRef]); +export const psbt_signing_cache_enable = wrap('wally_psbt_signing_cache_enable', [T.OpaqueRef, T.Int32]); export const psbt_to_base64 = wrap('wally_psbt_to_base64', [T.OpaqueRef, T.Int32, T.DestPtrPtr(T.String)]); export const ripemd160 = wrap('wally_ripemd160', [T.Bytes, T.DestPtrSized(T.Bytes, C.RIPEMD160_LEN)]); export const s2c_commitment_verify = wrap('wally_s2c_commitment_verify', [T.Bytes, T.Bytes, T.Bytes, T.Int32]); @@ -672,6 +674,7 @@ export const tx_get_input_issuance_amount_len = wrap('wally_tx_get_input_issuanc export const tx_get_input_issuance_amount_rangeproof_len = wrap('wally_tx_get_input_issuance_amount_rangeproof_len', [T.OpaqueRef, T.Int32, T.DestPtr(T.Int32)]); export const tx_get_input_script_len = wrap('wally_tx_get_input_script_len', [T.OpaqueRef, T.Int32, T.DestPtr(T.Int32)]); export const tx_get_input_sequence = wrap('wally_tx_get_input_sequence', [T.OpaqueRef, T.Int32, T.DestPtr(T.Int32)]); +export const tx_get_input_signature_hash = wrap('wally_tx_get_input_signature_hash', [T.OpaqueRef, T.Int32, T.OpaqueRef, T.OpaqueRef, T.OpaqueRef, T.Bytes, T.Int32, T.Int32, T.Bytes, T.Bytes, T.Int32, T.Int32, T.OpaqueRef, T.DestPtrSized(T.Bytes, C.SHA256_LEN)]); export const tx_get_input_txhash = wrap('wally_tx_get_input_txhash', [T.OpaqueRef, T.Int32, T.DestPtrSized(T.Bytes, C.WALLY_TXHASH_LEN)]); export const tx_get_input_witness_len = wrap('wally_tx_get_input_witness_len', [T.OpaqueRef, T.Int32, T.Int32, T.DestPtr(T.Int32)]); export const tx_get_input_witness_num_items = wrap('wally_tx_get_input_witness_num_items', [T.OpaqueRef, T.Int32, T.DestPtr(T.Int32)]); diff --git a/src/wasm_package/src/index.d.ts b/src/wasm_package/src/index.d.ts index 39e378f5e..eeb6fb648 100644 --- a/src/wasm_package/src/index.d.ts +++ b/src/wasm_package/src/index.d.ts @@ -562,6 +562,8 @@ export function psbt_set_version(psbt: Ref_wally_psbt, flags: number, version: n export function psbt_sign(psbt: Ref_wally_psbt, key: Buffer|Uint8Array, flags: number): void; export function psbt_sign_bip32(psbt: Ref_wally_psbt, hdkey: Ref_ext_key, flags: number): void; export function psbt_sign_input_bip32(psbt: Ref_wally_psbt, index: number, subindex: number, txhash: Buffer|Uint8Array, hdkey: Ref_ext_key, flags: number): void; +export function psbt_signing_cache_disable(psbt: Ref_wally_psbt): void; +export function psbt_signing_cache_enable(psbt: Ref_wally_psbt, flags: number): void; export function psbt_to_base64(psbt: Ref_wally_psbt, flags: number): string; export function ripemd160(bytes: Buffer|Uint8Array): Buffer; export function s2c_commitment_verify(sig: Buffer|Uint8Array, s2c_data: Buffer|Uint8Array, s2c_opening: Buffer|Uint8Array, flags: number): void; @@ -632,6 +634,7 @@ export function tx_get_input_issuance_amount_len(tx_in: Ref_wally_tx, index: num export function tx_get_input_issuance_amount_rangeproof_len(tx_in: Ref_wally_tx, index: number): number; export function tx_get_input_script_len(tx_in: Ref_wally_tx, index: number): number; export function tx_get_input_sequence(tx_in: Ref_wally_tx, index: number): number; +export function tx_get_input_signature_hash(tx: Ref_wally_tx, index: number, scripts: Ref_wally_map, assets: Ref_wally_map, values: Ref_wally_map, script: Buffer|Uint8Array, key_version: number, codesep_position: number, annex: Buffer|Uint8Array, genesis_blockhash: Buffer|Uint8Array, sighash: number, flags: number, cache: Ref_wally_map): Buffer; export function tx_get_input_txhash(tx_in: Ref_wally_tx, index: number): Buffer; export function tx_get_input_witness_len(tx_in: Ref_wally_tx, index: number, wit_index: number): number; export function tx_get_input_witness_num_items(tx_in: Ref_wally_tx, index: number): number; diff --git a/tools/wasm_exports.sh b/tools/wasm_exports.sh index f4f9b295d..e727d81ee 100644 --- a/tools/wasm_exports.sh +++ b/tools/wasm_exports.sh @@ -362,6 +362,8 @@ EXPORTED_FUNCTIONS="['_malloc','_free','_bip32_key_free' \ ,'_wally_psbt_sign' \ ,'_wally_psbt_sign_bip32' \ ,'_wally_psbt_sign_input_bip32' \ +,'_wally_psbt_signing_cache_disable' \ +,'_wally_psbt_signing_cache_enable' \ ,'_wally_psbt_to_base64' \ ,'_wally_psbt_to_bytes' \ ,'_wally_ripemd160' \ @@ -409,6 +411,7 @@ EXPORTED_FUNCTIONS="['_malloc','_free','_bip32_key_free' \ ,'_wally_tx_get_input_script' \ ,'_wally_tx_get_input_script_len' \ ,'_wally_tx_get_input_sequence' \ +,'_wally_tx_get_input_signature_hash' \ ,'_wally_tx_get_input_txhash' \ ,'_wally_tx_get_input_witness' \ ,'_wally_tx_get_input_witness_len' \