From fd6b0d2e3e47cb53930d40d48dcf24a342345c92 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Wed, 6 Sep 2023 16:16:35 +0100 Subject: [PATCH] Add BIP for OP_TXHASH and OP_CHECKTXHASHVERIFY --- README.mediawiki | 7 + bip-0346.md | 438 ++++++ bip-0346/ref-impl/Cargo.lock | 165 ++ bip-0346/ref-impl/Cargo.toml | 13 + bip-0346/ref-impl/src/main.rs | 695 +++++++++ bip-0346/ref-impl/txhash_vectors.json | 2015 +++++++++++++++++++++++++ 6 files changed, 3333 insertions(+) create mode 100644 bip-0346.md create mode 100644 bip-0346/ref-impl/Cargo.lock create mode 100644 bip-0346/ref-impl/Cargo.toml create mode 100644 bip-0346/ref-impl/src/main.rs create mode 100644 bip-0346/ref-impl/txhash_vectors.json diff --git a/README.mediawiki b/README.mediawiki index e89ccf5bff..f6ed5782e2 100644 --- a/README.mediawiki +++ b/README.mediawiki @@ -1134,6 +1134,13 @@ Those proposing changes should consider that ultimately consent may rest with th | Brandon Black, Jeremy Rubin | Standard | Draft +|- +| [[bip-0346.mediawiki|346]] +| Consensus (soft fork) +| OP_TXHASH and OP_TXHASHVERIFY +| Steven Roose, Brandon Black +| Standard +| Draft |- style="background-color: #cfffcf" | [[bip-0350.mediawiki|350]] | Applications diff --git a/bip-0346.md b/bip-0346.md new file mode 100644 index 0000000000..91750bffe5 --- /dev/null +++ b/bip-0346.md @@ -0,0 +1,438 @@ +``` + BIP: 346 + Layer: Consensus (soft fork) + Title: OP_TXHASH and OP_CHECKTXHASHVERIFY + Author: Steven Roose , Brandon Black + Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0346 + Status: Draft + Type: Standards Track + Created: 2023-09-03 + License: BSD-3-Clause +``` + +# Abstract + +This BIP proposes two new opcodes, `OP_CHECKTXHASHVERIFY`, to be activated +as a change to the semantics of `OP_NOP4` in legacy script, segwit and tapscript; +and `OP_TXHASH`, to be activated as a change to the semantics of `OP_SUCCESS189` +in tapscript only. + +These opcodes provide a generalized method for introspecting certain details of +the spending transaction, which enables non-interactive enforcement of certain +properties of the transaction spending a certain UTXO. + +The constructions specified in this BIP also open up the way for other +potential updates; see Motivation section for more details. + +As mentioned in the Deployment section, alternatively, the +`OP_CHECKTXHASHVERIFY` semantics could be used to upgrade an existing deployment +of the `OP_CHECKTEMPLATEVERIFY` opcode. The opcode `OP_TXHASH` could then +potentially be renamed to `OP_TEMPLATEHASH`. + + +# Summary + +## OP_CHECKTXHASHVERIFY + +The first new opcode, `OP_CHECKTXHASHVERIFY`, redefines the `OP_NOP4` opcode +(`0xb3`) as a soft fork upgrade. This opcode is active in both legacy script, +segwitv0 p2wsh and tapscript contexts. + +It has the following semantics: + +* There is at least one element on the stack, fail otherwise. +* The element on the stack is at least 32 bytes long, NOP otherwise. +* The first 32 bytes are interpreted as the StackTxHash and the remaining + suffix bytes specify the TxFieldSelector. +* The TxFieldSelector valid, fail otherwise. +* The TxHash of the transaction is calculated at the current input index using + the TxFieldSelector. +* The StackTxHash equals the TxHash, fail otherwise. + + +## OP_TXHASH + +The second new opcode, `OP_TXHASH`, redefines the `OP_SUCCESS189` tapscript +opcode (`0xbd`) as a soft fork upgrade. This opcode is only active in tapscript +context. + +It has the following semantics: + +* There is at least one element on the stack, fail otherwise. +* The element is interpreted as the TxFieldSelector and is popped off the stack. +* The TxFieldSelector must be valid, fail otherwise. +* The 32-byte TxHash of the transaction at the current input index, calculated + using the given TxFieldSelector is pushed onto the stack. + +## TxFieldSelector + +The TxFieldSelector has the following semantics. We will give a brief conceptual +summary, followed by a reference implementation of the CalculateTxHash function. + +* There are two special cases for the TxFieldSelector: + * the empty value, zero bytes long: it is set equal to `TXFS_SPECIAL_TEMPLATE`, + the de-facto default value which means everything except the prevouts and the prevout + scriptPubkeys and amounts. + + Special case `TXFS_SPECIAL_TEMPLATE` is 4 bytes long, as follows: + * 1: `TXFS_VERSION | TXFS_LOCKTIME | TXFS_CURRENT_INPUT_IDX` + * 2: `TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_OUTPUTS_ALL` + * 3: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + * 4: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + + * If the TxFieldSelector has exactly 1 byte, it has its 8 bits + assigned as follows, from lowest to highest: + * 2/1: Inputs + * 00: `TXFS_INOUT_SELECTION_NONE` + * 01: `TXFS_INOUT_SELECTION_CURRENT` + * 11: `TXFS_INOUT_SELECTION_ALL` + * 4/3: Outputs + * 00: `TXFS_INOUT_SELECTION_NONE` + * 01: `TXFS_INOUT_SELECTION_CURRENT` + * 11: `TXFS_INOUT_SELECTION_ALL` + * 5: `TXFS_INPUTS_PREVOUTS` + * 6: `TXFS_INPUTS_PREV_SCRIPTPUBKEYS | TXFS_INPUTS_PREV_VALUES` + * 7: `TXFS_CURRENT_INPUT_CONTROL_BLOCK | TXFS_CURRENT_INPUT_SPENTSCRIPT | TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS` + * 8: `TXFS_CURRENT_INPUT_IDX` + + Additionally, it includes `TXFS_VERSION | TXFS_LOCKTIME | TXFS_CONTROL | TXFS_CURRENT_INPUT_TAPROOT_ANNEX | TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_OUTPUTS_ALL` + + These 1-byte selections allow the TxFieldSelector to emulate current + signature hashing modes and those defined in BIP-118: + +| BIP-341/118 sighash type | 1-byte TxFieldSelector | +| :--------------------------- | :--------------------- | +| `ALL` | `0b11111111` | +| `SINGLE` | `0b11110111` | +| `NONE` | `0b11110011` | +| `ALL|ANYONECANPAY` | `0b11111101` | +| `SINGLE|ANYONECANPAY` | `0b11110101` | +| `NONE|ANYONECANPAY` | `0b11110001` | +| `ALL|ANYPREVOUT` | `0b11101101` | +| `SINGLE|ANYPREVOUT` | `0b11100101` | +| `NONE|ANYPREVOUT` | `0b11100001` | +| `ALL|ANYPREVOUTANYSCRIPT` | `0b11001101` | +| `SINGLE|ANYPREVOUTANYSCRIPT` | `0b11000101` | +| `NONE|ANYPREVOUTANYSCRIPT` | `0b11000001` | + +* The first byte of the TxFieldSelector has its 8 bits assigned as follows, from lowest to highest: + * 1: version (`TXFS_VERSION`) + * 2: locktime (`TXFS_LOCKTIME`) + * 3: current input index (`TXFS_CURRENT_INPUT_IDX`) + * 4: current input control block (or empty) (`TXFS_CURRENT_INPUT_CONTROL_BLOCK`) + * 5: current input spent script (i.e. witness script or tapscript) (`TXFS_CURRENT_INPUT_SPENTSCRIPT`) + * 6: current script last `OP_CODESEPARATOR` position (or 0xffffffff) + (`TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS`) + * 7: current input annex (or empty) (`TXFS_CURRENT_INPUT_TAPROOT_ANNEX`) + * 8: `TXFS_CONTROL` (i.e. include TxFieldSelector into hash) + +* The highest bit of the first byte (`TXFS_CONTROL`), we will call the + "control bit", and it can be used to control the behavior of the opcode. For + `OP_TXHASH` and `OP_CHECKTXHASHVERIFY`, the control bit is used to determine + whether the TxFieldSelector itself has to be included in the resulting hash. + (For potential other uses of the TxFieldSelector (like a hypothetical + `OP_TX`), this bit can be repurposed.) + +* The second byte will be used to indicate fields from the inputs and outputs. + If there is only a single byte present, no information from the inputs and + outputs will be committed. Otherwise, of the second byte, the 8 bits are + assigned the following variables, from lowest to highest: + * Specifying which fields of the inputs will be selected: + * 1: prevouts (`TXFS_INPUTS_PREVOUTS`) + * 2: sequences (`TXFS_INPUTS_SEQUENCES`) + * 3: scriptSigs (`TXFS_INPUTS_SCRIPTSIGS`) + * 4: prevout scriptPubkeys (`TXFS_INPUTS_PREV_SCRIPTPUBKEYS`) + * 5: prevout values (`TXFS_INPUTS_PREV_VALUES`) + * 6: taproot annexes (`TXFS_INPUTS_TAPROOT_ANNEXES`) + + * Specifying which fields of the outputs will be selected: + * 7: scriptPubkeys (`TXFS_OUTPUTS_SCRIPTPUBKEYS`) + * 8: values (`TXFS_OUTPUTS_VALUES`) + +* We define as follows: + * `TXFS_ALL = TXFS_VERSION | TXFS_LOCKTIME | TXFS_CURRENT_INPUT_IDX | TXFS_CURRENT_INPUT_CONTROL_BLOCK | TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS | TXFS_CONTROL` + * `TXFS_INPUTS_ALL = TXFS_INPUTS_PREVOUTS | TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_INPUTS_PREV_SCRIPTPUBKEYS | TXFS_INPUTS_PREV_VALUES | TXFS_INPUTS_TAPROOT_ANNEXES` + * `TXFS_OUTPUTS_ALL = TXFS_OUTPUTS_SCRIPTPUBKEYS | TXFS_OUTPUTS_VALUES` + + +* For both inputs and then outputs, expect an additional byte as follows: + * The highest bit (`TXFS_INOUT_NUMBER`) indicates whether the "number of + in-/outputs" should be committed to. + * For the remaining bits, there are three exceptional values: + * 0x00 (`TXFS_INOUT_SELECTION_NONE`) means "no in/outputs" (hence only the + number of them as `0x80` (`TXFS_INOUT_NUMBER`)). + * `0x40` (`TXFS_INOUT_SELECTION_CURRENT`) means "select only the in/output + of the current input index" (it is invalid when current index exceeds + number of outputs). + * `0x3f` (`TXFS_INOUT_SELECTION_ALL`) means "select all in/outputs". + + * The second highest bit (`TXFS_INOUT_SELECTION_MODE`) is the "specification mode": + * Set to 0 it means "leading mode". + * Set to 1 it means "individual mode". + + * In "leading mode", the third highest bit (`TXFS_INOUT_LEADING_SIZE`) is + used to indicate the "index size", i.e. the number of bytes will be used to + represent the number of in/output. + * With "index size" set to 0, the remaining lowest 5 bits of the first byte + will be interpreted as the number of leading in/outputs to select. + * With "index size" set to 1, the remaining lowest 5 bits of the first byte + together with the 8 bits of the next byte will be interpreted as the + number of leading in/outputs to select. + + * In "individual mode", the third highest bit (`TXFS_INOUT_INDIVIDUAL_MODE`) + indicates whether we are passing absolute indices (0) or indices relative + to the current input (1), the remaining lowest 5 bits will be interpreted + as `n`, the number of individual in/outputs follow. + * In absolute mode (second highest bit is 0), for each of the `n` indices, + at least one extra byte is expected. + * If that byte's highest bit is set to 0, the remaining 7 bits represent + the absolute index to select. + * If that byte's highest bit is set to 1, the remaining 7 bits, together + with the next byte's 8 bits represent the absolute index to select. + * In relative mode (second highest bit is 1), for each of the `n` indices, + at least one extra byte is expected. + * If that byte's highest bit is set to 0, the remaining 7 bits represent + the relative index in two's complement. + * If that byte's highest bit is set to 1, the remaining 7 bits, together + with the next byte's 8 bits represent the relative index in two's + complement. + + +Effectively, this allows a user to select +* all in/outputs +* the current input index +* the leading in/outputs up to 7936 +* up to 32 individually selected in/outputs +** using absolute indices up to 16384 +** using indices relative to the current input index from -8191 to +8192. + + +### TxFieldSelector malleability + +It is possible to represent the same selected data using multiple different +TxFieldSelectors. For this reason, users are strongly advised to always set the +`TXFS_CONTROL` that commits to the TxFieldSelector that was used to get the +hash. + + + +### Visualization + +* first byte + +``` +1 0 1 1 1 1 1 1 +| | | | | | | ^ version +| | | | | | ^ locktime +| | | | | ^ current input index +| | | | ^ current input control block +| | | ^ current input spend script +| | ^ current script last OP_CODESEPARATOR +| ^ unused +^ control bit (ie. include TXFS in hash) +``` + +* second byte + +``` +<-> outputs +| | <---------> inputs +1 1 1 1 1 1 1 1 +| | | | | | | ^ prevouts +| | | | | | ^ sequences +| | | | | ^ scriptSigs +| | | | ^ prevout scriptPubkeys +| | | ^ prevout values +| | ^ taproot annexes +| ^ scriptPubkeys +^ values +``` + +* in/output selector byte + +"only the first 3" +``` +1 0 0 0 0 0 1 1 +| | | <-------> integer 0b00011 == 3 +| | ^ index size 0: single byte +| ^ leading mode +^ commit the number of in/outputs +``` + +"only the first 257" +``` +1 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 +| | | <------------------------> integer 0b00001 00000001 == 257 +| | ^ index size 1: two bytes +| ^ leading mode +^ commit the number of in/outputs +``` + +"only indices 0 and 2" +``` +0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 +| | | | <--------------> second idx: 3 +| | | | <-------------> first idx: 1 +| | | | <-----> selection count: 0b10 == 2 +| | | ^ index size 0: single byte per index +| | ^ absolute index +| ^ individual mode +^ don't commit the number of in/outputs +``` + +* total example + +``` +bf ff c2 01 03 83 + | | ^ commit number of outputs + leading 3 outputs + | | <------> commit number of inputs + inputs at indices 1 and 3 + | ^ all input and output fields + ^ all regular fields, except for the unused one +``` + + +## Resource limits + +* For legacy scripts and segwit, we don't add any extra resource limitations, + with the argumentation that `OP_CHECKTXHASHVERIFY` already requires the user + to provide at least 32 bytes of extra transaction size, either in the input + scriptSig, or the witness. Additional more complex hashes require additional + witness bytes. Given that `OP_CAT` is not available in this context, if a + malicious user tries to increase the number of TransactionHashes being + calculated by using opcodes like `OP_DUP`, the TxFieldSelector for all these + calculations is identical, so the calculation can be cached within the same + transaction. + +* For tapscript, primarily motivated by the cheaper opcode `OP_TXHASH` (it + doesn't require an additional 32 witness bytes be provided) and the potential + future addition of byte manipulation opcodes like `OP_CAT`, an additional + cost is specified per TransactionHash execution. Using the same validation + budget ("sigops budget") introduced in BIP-0342, each TransactionHash + decreases the validation budget by 25. If this brings the budget below zero, + the script fails immediately.
The following considerations should be made: + * All fields that can be of arbitrary size are cachable as TransactionHash + always hashes their hashed values. + * In "individual mode", a user can at most commit 32 inputs or outputs, + which we don't consider excessive for potential repeated use. + * In "leading mode", a caching strategy can be used where the SHA256 context + is stored every N in/outputs so that multiple executions of the + TransactionHash function can use the caches and only have to hash an + additional N-1 items at most. + + +# Motivation + +This BIP specifies a basic transaction introspection primitive that is useful +to either reduce interactivity in multi-user protocols or to enforce some basic +constraints on transactions. + +Additionally, the constructions specified in this BIP can lay the groundwork for +some potential future upgrades: +* The TxFieldSelector construction would work well with a hypothetical opcode + `OP_TX` that allows for directly introspecting the transaction by putting the + fields selected on the stack instead of hashing them together. +* The TransactionHash obtained by `OP_TXHASH` can be combined with a + hypothetical opcode `OP_CHECKSIGFROMSTACK` to effectively create an + incredibly flexible signature hash, which would enable constructions like + `SIGHASH_ANYPREVOUT`. + + +## Comparing with some alternative proposals + +* This proposal strictly generalizes BIP-119's `OP_CHECKTEMPLATEVERIFY`, as the + default mode of our TxFieldSelector is semantically the same (though not + byte-for-byte identical) as what `OP_CTV` accomplishes, without costing any + additional bytes. Additionally, using `OP_CHECKTXHASHVERIFY` allows for more + flexibility which can help in the case for + * enabling adding fees to a transaction without breaking a multi-tx protocol; + * multi-user protocols where users are only concerned about their own inputs and outputs. + +* Constructions like `OP_IN_OUT_VALUE` used with `OP_EQUALVERIFY` can be + emulated by two `OP_TXHASH` instances by using the TxFieldSelector to select + a single input value first and a single output value second and enforcing + equality on the hashes. Neither of these alternatives can be used to enforce + small value differencials without the availability of 64-bit arithmetic in + Script. + +* Like mentioned above, `SIGHASH_ANYPREVOUT` can be emulated using `OP_TXHASH` + when combined with `OP_CHECKSIGFROMSTACK`: + ` OP_TXHASH OP_CHECKSIGFROMSTACK` effectively emulates `SIGHASH_ANYPREVOUT`. + + +# Detailed Specification + +A reference implementation in Rust is provided attached as part of this BIP +together with a JSON file of test vectors generated using the reference +implementation. + + +# Backwards Compatibility + +`OP_CHECKTXHASHVERIFY` replaces `OP_NOP4` with stricter verification semantics. +Therefore, scripts which previously were valid will cease to be valid with this +change. Stricter verification semantics for an `OP_NOP` are a soft fork, so +existing software will be fully functional without upgrade except for mining and +block validation. Similar soft forks for `OP_CHECKSEQUENCEVERIFY` and +`OP_CHECKLOCKTIMEVERIFY` (see BIP-0065 and BIP-0112) have similarly changed +`OP_NOP` semantics without introducing compatibility issues. +`OP_CHECKTXHASHVERIFY`, as the other opcodes ending in `-VERIFY`, leaves the +stack intact, and instead halts execution with a failure if the condition +expressed using the opcode was not met. + +`OP_TXHASH`, instead, replaces `OP_SUCCESS189`. The `SUCCESS` opcodes were +introduced in taproot (BIP-342) to support changing the semantics of opcodes in +ways that do allow the new semantics to change the stack. For this reason, +`OP_TXHASH` only works in tapscript context. Since it is overriding a `SUCCESS` +opcode, any older version of the software will always accept any script that +uses the opcode, while the new versions of the software will validate the +scripts according to the semantics outlined in this BIP. As such, this is also a +soft fork change. + + +# Implementation + +* A proposed implementation for Bitcoin Core is available here: + https://github.com/bitcoin/bitcoin/pull/29050 +* A proposed implementation for rust-bitcoin is available here: + https://github.com/rust-bitcoin/rust-bitcoin/pull/2275 + +Both of the above implementations perform effective caching to avoid potential +denial-of-service attack vectors. + + +# Deployment + +This BIP can be deployed using a BIP 9 VersionBits deployment. The specific +strategy and bit assignment are left unspecified and can later be amended to the +BIP depending on community preference. + + +## OP_CHECKTEMPLATEVERIFY (BIP-119) + +BIP 119 described an opcode called `OP_CHECKTEMPLATEVERIFY` (often dubbed +"CTV"), which also overrides the same `OP_NOP4` that this BIP uses for +`OP_CHECKTXHASHVERIFY`. This is no coincidence: in the case of BIP 119 +activation before the deployment of this BIP, the logic in this BIP can be seen +as an upgrade to the CTV opcode. The following is the only required change in +that scenario: + +- We drop the definition of `TXFS_SPECIAL_TEMPLATE` and the special case of the +empty TxFieldSelector will use BIP-119's DefaultCheckTemplateVerifyHash as the +TxHash. Similarly, `OP_TXHASH` with the empty TxFieldSelector will put the +DefaultCheckTemplateVerifyHash on the stack. + +Depending on community preference, in this scenario, the `OP_CHECKTXHASHVERIFY` +opcode could keep the name `OP_CHECKTEMPLATEVERIFY` to not have two different +names for the same opcode. Equivalently, `OP_TXHASH` could then be renamed to +`OP_TEMPLATEHASH`. + + +# Acknowledgement + +Credit for this proposal mostly goes to Jeremy Rubin for his work on BIP-119's +`OP_CHECKTEMPLATEVERIFY` and to Russell O'Connor for the original idea of +generalizing `OP_CHECKTEMPLATEVERIFY` into `OP_TXHASH`. + +Additional thanks to Andrew Poelstra, Greg Sanders, Rearden Code, Rusty Russell +and others for their feedback on the specification. + diff --git a/bip-0346/ref-impl/Cargo.lock b/bip-0346/ref-impl/Cargo.lock new file mode 100644 index 0000000000..20b3eab790 --- /dev/null +++ b/bip-0346/ref-impl/Cargo.lock @@ -0,0 +1,165 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bech32" +version = "0.10.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" + +[[package]] +name = "bitcoin" +version = "0.31.0" +source = "git+https://github.com/stevenroose/rust-bitcoin.git?branch=txhash#5fcf0cba24c1b12e1234f71971cfadcc03cf6254" +dependencies = [ + "bech32", + "bitcoin-internals", + "bitcoin_hashes", + "hex-conservative", + "hex_lit", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "git+https://github.com/stevenroose/rust-bitcoin.git?branch=txhash#5fcf0cba24c1b12e1234f71971cfadcc03cf6254" +dependencies = [ + "hex-conservative", + "serde", +] + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "secp256k1" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +dependencies = [ + "bitcoin_hashes", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "txhash-ref" +version = "0.0.0" +dependencies = [ + "bitcoin", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/bip-0346/ref-impl/Cargo.toml b/bip-0346/ref-impl/Cargo.toml new file mode 100644 index 0000000000..05d02e30ff --- /dev/null +++ b/bip-0346/ref-impl/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "txhash-ref" +version = "0.0.0" +edition = "2021" + +[dependencies] +bitcoin = { version = "=0.31.0", features = [ "serde" ] } +serde_json = "1.0.108" + +# until bitcoin-io is released and https://github.com/rust-bitcoin/rust-bitcoin/pull/2274 is merged +[patch.crates-io] +bitcoin = { git = "https://github.com/stevenroose/rust-bitcoin.git", branch = "txhash", features = [ "serde" ] } +bitcoin_hashes = { git = "https://github.com/stevenroose/rust-bitcoin.git", branch = "txhash" } diff --git a/bip-0346/ref-impl/src/main.rs b/bip-0346/ref-impl/src/main.rs new file mode 100644 index 0000000000..df24f16ee8 --- /dev/null +++ b/bip-0346/ref-impl/src/main.rs @@ -0,0 +1,695 @@ + +use bitcoin::{Transaction, TxOut}; +use bitcoin::consensus::encode::Encodable; +use bitcoin::hashes::{sha256, Hash, HashEngine}; + +pub const TXFS_VERSION: u8 = 1 << 0; +pub const TXFS_LOCKTIME: u8 = 1 << 1; +pub const TXFS_CURRENT_INPUT_IDX: u8 = 1 << 2; +pub const TXFS_CURRENT_INPUT_SPENTSCRIPT: u8 = 1 << 3; +pub const TXFS_CURRENT_INPUT_CONTROL_BLOCK: u8 = 1 << 4; +pub const TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS: u8 = 1 << 5; +pub const TXFS_CURRENT_INPUT_TAPROOT_ANNEX: u8 = 1 << 6; +pub const TXFS_CONTROL: u8 = 1 << 7; + +pub const TXFS_INPUTS_PREVOUTS: u8 = 1 << 0; +pub const TXFS_INPUTS_SEQUENCES: u8 = 1 << 1; +pub const TXFS_INPUTS_SCRIPTSIGS: u8 = 1 << 2; +pub const TXFS_INPUTS_PREV_SCRIPTPUBKEYS: u8 = 1 << 3; +pub const TXFS_INPUTS_PREV_VALUES: u8 = 1 << 4; +pub const TXFS_INPUTS_TAPROOT_ANNEXES: u8 = 1 << 5; +pub const TXFS_OUTPUTS_SCRIPTPUBKEYS: u8 = 1 << 6; +pub const TXFS_OUTPUTS_VALUES: u8 = 1 << 7; + +pub const TXFS_INPUTS_ALL: u8 = TXFS_INPUTS_PREVOUTS + | TXFS_INPUTS_SEQUENCES + | TXFS_INPUTS_SCRIPTSIGS + | TXFS_INPUTS_PREV_SCRIPTPUBKEYS + | TXFS_INPUTS_PREV_VALUES + | TXFS_INPUTS_TAPROOT_ANNEXES; +pub const TXFS_OUTPUTS_ALL: u8 = TXFS_OUTPUTS_SCRIPTPUBKEYS | TXFS_OUTPUTS_VALUES; + +pub const TXFS_INOUT_NUMBER: u8 = 1 << 7; +pub const TXFS_INOUT_SELECTION_NONE: u8 = 0x00; +pub const TXFS_INOUT_SELECTION_CURRENT: u8 = 0x40; +pub const TXFS_INOUT_SELECTION_ALL: u8 = 0x3f; +pub const TXFS_INOUT_SELECTION_MODE: u8 = 1 << 6; +pub const TXFS_INOUT_LEADING_SIZE: u8 = 1 << 5; +pub const TXFS_INOUT_INDIVIDUAL_MODE: u8 = 1 << 5; +pub const TXFS_INOUT_SELECTION_MASK: u8 = 0xff ^ (1 << 7) ^ (1 << 6) ^ (1 << 5); + + +pub const TXFS_SPECIAL_TEMPLATE: [u8; 4] = [ + TXFS_VERSION | TXFS_LOCKTIME | TXFS_CURRENT_INPUT_IDX, + TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_OUTPUTS_ALL, + TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL, + TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL, +]; + +const SHA256_EMPTY: sha256::Hash = sha256::Hash::const_hash(&[]); + +fn read_i7(input: u8) -> i8 { + let masked = input & 0x7f; + if (masked & 0x40) == 0 { + masked as i8 + } else { + 0i8 - ((!(masked-1)) & 0x7f) as i8 + } +} + +fn read_i15(input: u16) -> i16 { + let masked = input & 0x7fff; + if (masked & 0x4000) == 0 { + masked as i16 + } else { + 0i16 - ((!(masked-1)) & 0x7fff) as i16 + } +} + +/// Parse an input or output selection from the TxFieldSelector bytes. +/// +/// Returns the selected indices and a flag whether to commit the number of items. +fn parse_inout_selection( + bytes: &mut impl Iterator, + nb_items: usize, + current_input_idx: u32, + allow_empty: bool, +) -> Result<(Vec, bool), &'static str> { + let first = match bytes.next() { + Some(b) => b, + None if !allow_empty => return Ok((vec![], false)), + None /* if allow_empty */ => return Err("byte missing instead of empty selection"), + }; + let commit_number = (first & TXFS_INOUT_NUMBER) != 0; + let selection = first & (0xff ^ TXFS_INOUT_NUMBER); + + let selected = if selection == TXFS_INOUT_SELECTION_NONE { + vec![] + } else if selection == TXFS_INOUT_SELECTION_ALL { + (0..nb_items).collect() + } else if selection == TXFS_INOUT_SELECTION_CURRENT { + if current_input_idx as usize >= nb_items { + // NB can only happen for outputs + return Err("current input index exceeds number of outputs and current output selected"); + } + vec![current_input_idx as usize] + } else if (selection & TXFS_INOUT_SELECTION_MODE) == 0 { // leading mode + let count = if (selection & TXFS_INOUT_LEADING_SIZE) == 0 { + (selection & TXFS_INOUT_SELECTION_MASK) as usize + } else { + let next_byte = bytes.next().ok_or("second leading selection byte missing")?; + (((selection & TXFS_INOUT_SELECTION_MASK) as usize) << 8) + next_byte as usize + }; + assert_ne!(count, 0, "this should be interpreted as NONE above"); + if count > nb_items { + return Err("selected number of leading in/outputs out of bounds"); + } + (0..count).collect() + } else { // individual mode + let absolute = (selection & TXFS_INOUT_INDIVIDUAL_MODE) == 0; + + let count = (selection & TXFS_INOUT_SELECTION_MASK) as usize; + + let mut selected = Vec::with_capacity(count as usize); + for _ in 0..count { + let first = bytes.next().ok_or("expected an index byte")?; + let single_byte = (first & (1 << 7)) == 0; + let number = if single_byte { + first as usize + } else { + let next_byte = bytes.next().ok_or("expected another index byte")?; + (((first & (1 << 7)) as usize) << 8) + next_byte as usize + }; + + let idx = if absolute { + number + } else { + let rel = if single_byte { + read_i7(number as u8) as isize + } else { + read_i15(number as u16) as isize + }; + + if rel.is_negative() && rel.abs() > current_input_idx as isize { + return Err("relative index out of bounds"); + } + (current_input_idx as isize + rel) as usize + }; + + if idx > nb_items { + return Err("selected index out of bounds"); + } + if let Some(last) = selected.last() { + if idx <= *last { + return Err("selected indices not in increasing order") + } + } + selected.push(idx); + } + selected + }; + Ok((selected, commit_number)) +} + +fn convert_short_txfs(txfs: u8) -> Result, &'static str> { + //TODO(stevenroose) current input annex?? + let mut base = TXFS_VERSION | TXFS_LOCKTIME | TXFS_CONTROL | TXFS_CURRENT_INPUT_TAPROOT_ANNEX; + let mut inout_fields = TXFS_OUTPUTS_ALL | TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS; + + let input_selection = match txfs & 0b00000011 { + 0b00000000 => TXFS_INOUT_SELECTION_NONE, + 0b00000001 => TXFS_INOUT_SELECTION_CURRENT, + 0b00000011 => TXFS_INOUT_SELECTION_ALL, + _ => return Err("0b10 is not a valid input selection"), + }; + let output_selection = match txfs & 0b00001100 { + 0b00000000 => TXFS_INOUT_SELECTION_NONE, + 0b00000100 => TXFS_INOUT_SELECTION_CURRENT, + 0b00001100 => TXFS_INOUT_SELECTION_ALL, + _ => return Err("0b10 is not a valid output selection"), + }; + + if txfs & 0b00010000 != 0 { + inout_fields = inout_fields | TXFS_INPUTS_PREVOUTS; + } + + if txfs & 0b00100000 != 0 { + inout_fields = inout_fields | TXFS_INPUTS_PREV_SCRIPTPUBKEYS | TXFS_INPUTS_PREV_VALUES; + } + + if txfs & 0b01000000 != 0 { + base = base | TXFS_CURRENT_INPUT_CONTROL_BLOCK | TXFS_CURRENT_INPUT_SPENTSCRIPT + | TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS; + } + + if txfs & 0b10000000 != 0 { + base = base | TXFS_CURRENT_INPUT_IDX; + } + + Ok(vec![base, inout_fields, input_selection, output_selection]) +} + +/// +/// +/// Assumes that TxFieldSelector is valid. +pub fn calculate_txhash( + txfs: &[u8], + tx: &Transaction, + prevouts: &[TxOut], + current_input_idx: u32, + current_input_last_codeseparator_pos: Option, +) -> Result { + assert_eq!(tx.input.len(), prevouts.len()); + + let txfs = if txfs.is_empty() { + TXFS_SPECIAL_TEMPLATE.to_vec() + } else if txfs.len() == 1 { + convert_short_txfs(txfs[0])? + } else { + txfs.to_vec() + }; + let txfs = &txfs; + + let mut engine = sha256::Hash::engine(); + + if (txfs[0] & TXFS_CONTROL) != 0 { + engine.input(txfs); + } + + let mut bytes = txfs.iter().copied().peekable(); + let global = bytes.next().unwrap(); + + if (global & TXFS_VERSION) != 0 { + tx.version.consensus_encode(&mut engine).unwrap(); + } + + if (global & TXFS_LOCKTIME) != 0 { + tx.lock_time.consensus_encode(&mut engine).unwrap(); + } + + if (global & TXFS_CURRENT_INPUT_IDX) != 0 { + (current_input_idx as u32).consensus_encode(&mut engine).unwrap(); + } + + let cur = current_input_idx as usize; + if (global & TXFS_CURRENT_INPUT_SPENTSCRIPT) != 0 { + let ss = if prevouts[cur].script_pubkey.is_p2sh() { + tx.input[cur].script_sig.redeem_script().map(|s| s.as_bytes()).unwrap_or(&[]) + } else if prevouts[cur].script_pubkey.is_p2wsh() { + tx.input[cur].witness.witness_script().map(|s| s.as_bytes()).unwrap_or(&[]) + } else if prevouts[cur].script_pubkey.is_p2tr() { + tx.input[cur].witness.tapscript().map(|s| s.as_bytes()).unwrap_or(&[]) + } else { + &[] + }; + engine.input(&sha256::Hash::hash(&ss)[..]); + } + + if (global & TXFS_CURRENT_INPUT_CONTROL_BLOCK) != 0 { + let cb = if prevouts[cur].script_pubkey.is_p2tr() { + tx.input[cur].witness.taproot_control_block().unwrap_or(&[]) + } else { + &[] + }; + engine.input(&sha256::Hash::hash(&cb)[..]); + } + + if (global & TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS) != 0 { + let pos = current_input_last_codeseparator_pos.unwrap_or(u32::MAX); + (pos as u32).consensus_encode(&mut engine).unwrap(); + } + + if (global & TXFS_CURRENT_INPUT_TAPROOT_ANNEX) != 0 { + if let Some(annex) = tx.input[cur].witness.taproot_annex() { + engine.input(&sha256::Hash::hash(annex)[..]); + } else { + engine.input(&SHA256_EMPTY[..]); + } + } + + let inout_fields = match bytes.next() { + Some(b) => b, + // Stop early if no inputs or outputs are selected. + None => return Ok(sha256::Hash::from_engine(engine)), + }; + + // Inputs + let (input_select, commit_number_inputs) = parse_inout_selection( + &mut bytes, tx.input.len(), current_input_idx, true, + )?; + + if commit_number_inputs { + (tx.input.len() as u32).consensus_encode(&mut engine).unwrap(); + } + + if !input_select.is_empty() && (inout_fields & TXFS_INPUTS_PREVOUTS) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &input_select { + tx.input[*i].previous_output.consensus_encode(&mut engine).unwrap(); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !input_select.is_empty() && (inout_fields & TXFS_INPUTS_SEQUENCES) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &input_select { + tx.input[*i].sequence.consensus_encode(&mut engine).unwrap(); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !input_select.is_empty() && (inout_fields & TXFS_INPUTS_SCRIPTSIGS) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &input_select { + engine.input(&sha256::Hash::hash(&tx.input[*i].script_sig.as_bytes())[..]); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !input_select.is_empty() && (inout_fields & TXFS_INPUTS_PREV_SCRIPTPUBKEYS) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &input_select { + engine.input(&sha256::Hash::hash(&prevouts[*i].script_pubkey.as_bytes())[..]); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !input_select.is_empty() && (inout_fields & TXFS_INPUTS_PREV_VALUES) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &input_select { + prevouts[*i].value.consensus_encode(&mut engine).unwrap(); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !input_select.is_empty() && (inout_fields & TXFS_INPUTS_TAPROOT_ANNEXES) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &input_select { + if prevouts[*i].script_pubkey.is_p2tr() { + if let Some(annex) = tx.input[*i].witness.taproot_annex() { + engine.input(&sha256::Hash::hash(annex)[..]); + } else { + engine.input(&SHA256_EMPTY[..]); + } + } else { + engine.input(&SHA256_EMPTY[..]); + } + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + // Outputs + if bytes.peek().is_none() { + } else { + let allow_empty = (inout_fields & TXFS_OUTPUTS_ALL) == 0; + let (selection, commit_number) = parse_inout_selection( + &mut bytes, tx.output.len(), current_input_idx, allow_empty, + )?; + + if commit_number { + (tx.output.len() as u32).consensus_encode(&mut engine).unwrap(); + } + + if !selection.is_empty() && (inout_fields & TXFS_OUTPUTS_SCRIPTPUBKEYS) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &selection { + engine.input(&sha256::Hash::hash(&tx.output[*i].script_pubkey.as_bytes())[..]); + } + sha256::Hash::from_engine(engine) + }; + hash.consensus_encode(&mut engine).unwrap(); + } + + if !selection.is_empty() && (inout_fields & TXFS_OUTPUTS_VALUES) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &selection { + tx.output[*i].value.consensus_encode(&mut engine).unwrap(); + } + sha256::Hash::from_engine(engine) + }; + hash.consensus_encode(&mut engine).unwrap(); + } + } + + if bytes.next().is_some() { + return Err("unused txfs bytes"); + } + Ok(sha256::Hash::from_engine(engine)) +} + +mod test_vectors { + use super::*; + use std::any::Any; + use std::ops::{self, RangeBounds}; + use bitcoin::hex::DisplayHex; + use bitcoin::{Amount, ScriptBuf, Sequence, Witness}; + use bitcoin::blockdata::transaction::{self, TxIn}; + use bitcoin::opcodes::all::*; + + fn test_vector_tx() -> (Transaction, Vec) { + let tx = Transaction { + version: transaction::Version::TWO, + lock_time: bitcoin::absolute::LockTime::from_consensus(42), + input: vec![ + TxIn { + previous_output: "1111111111111111111111111111111111111111111111111111111111111111:1".parse().unwrap(), + script_sig: vec![0x23].into(), + sequence: Sequence::from_consensus(1), + witness: Witness::new(), + }, + TxIn { + previous_output: "2222222222222222222222222222222222222222222222222222222222222222:2".parse().unwrap(), + script_sig: ScriptBuf::new(), + sequence: Sequence::from_consensus(3), + witness: { // p2wsh annex-like stack element + let mut buf = Witness::new(); + buf.push(vec![0x13]); + buf.push(vec![0x14]); + buf.push(vec![0x50, 0x42]); // annex + buf + }, + }, + TxIn { + previous_output: "3333333333333333333333333333333333333333333333333333333333333333:3".parse().unwrap(), + script_sig: ScriptBuf::new(), + sequence: Sequence::from_consensus(2), + witness: { + let mut buf = Witness::new(); + buf.push(vec![0x12]); + buf + }, + }, + TxIn { + previous_output: "4444444444444444444444444444444444444444444444444444444444444444:4".parse().unwrap(), + script_sig: ScriptBuf::new(), + sequence: Sequence::from_consensus(3), + witness: { + let mut buf = Witness::new(); + buf.push(vec![0x13]); + buf.push(vec![0x14]); + buf.push(vec![0x50, 0x42]); // annex + buf + }, + }, + ], + output: vec![ + TxOut { + script_pubkey: vec![OP_PUSHNUM_6.to_u8()].into(), + value: Amount::from_sat(350), + }, + TxOut { + script_pubkey: vec![OP_PUSHNUM_7.to_u8()].into(), + value: Amount::from_sat(351), + }, + TxOut { + script_pubkey: vec![OP_PUSHNUM_8.to_u8()].into(), + value: Amount::from_sat(353), + }, + ], + }; + let prevs = vec![ + TxOut { + script_pubkey: vec![OP_PUSHNUM_16.to_u8()].into(), + value: Amount::from_sat(360), + }, + TxOut { + script_pubkey: vec![ // p2wsh + 0x00, 0x20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ].into(), + value: Amount::from_sat(361), + }, + TxOut { + script_pubkey: vec![ // p2tr + 0x51, 0x20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ].into(), + value: Amount::from_sat(361), + }, + TxOut { + script_pubkey: vec![ // p2tr + 0x51, 0x20, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ].into(), + value: Amount::from_sat(362), + }, + ]; + (tx, prevs) + } + + #[derive(Debug)] + struct TestCase { + tx: Transaction, + prevs: Vec, + vectors: Vec + } + + #[derive(Debug)] + struct TestVector { + txfs: Vec, + input: usize, + codeseparator: Option, + txhash: sha256::Hash, + } + + fn generate_vectors() -> Vec { + let all = 0xff; + let allio = TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL; + let selnone = TXFS_INOUT_SELECTION_NONE; // 0x00 + let selcur = TXFS_INOUT_SELECTION_CURRENT; + let selall = TXFS_INOUT_SELECTION_ALL; + let number = TXFS_INOUT_NUMBER; + let leading = 0; + let individual = TXFS_INOUT_SELECTION_MODE; + let absolute = 0; + let relative = TXFS_INOUT_INDIVIDUAL_MODE; + + let nbout = 3; + + fn r + 'static>(t: T) -> Box { + Box::new(t) + } + + // txfs and range of inputs to run it on + let selectors: &[(&[u8], Box)] = &[ + // global + (&[1 << 0, 0, 0, 0], r(..)), + (&[1 << 1, 0, 0, 0], r(..)), + (&[1 << 2, 0, 0, 0], r(..)), + (&[1 << 3, 0, 0, 0], r(..)), + (&[1 << 4, 0, 0, 0], r(..)), + (&[1 << 5, 0, 0, 0], r(..)), + (&[1 << 6, 0, 0, 0], r(..)), + (&[1 << 7, 0, 0, 0], r(..)), + // outputs + (&[all, 0, 0, number | selnone], r(..)), + (&[all, TXFS_OUTPUTS_SCRIPTPUBKEYS, 0, selcur], r(..nbout)), + (&[all, TXFS_OUTPUTS_VALUES, 0, selcur], r(..nbout)), + (&[all, TXFS_OUTPUTS_ALL, 0, selcur], r(..nbout)), + (&[all, TXFS_OUTPUTS_SCRIPTPUBKEYS, 0, selall], r(..)), + (&[all, TXFS_OUTPUTS_VALUES, 0, selall], r(..)), + (&[all, TXFS_OUTPUTS_ALL, 0, selall], r(..)), + (&[all, TXFS_OUTPUTS_SCRIPTPUBKEYS, 0, number | selcur], r(..nbout)), + (&[all, TXFS_OUTPUTS_VALUES, 0, number | selcur], r(..nbout)), + (&[all, TXFS_OUTPUTS_ALL, 0, number | selcur], r(..nbout)), + (&[all, TXFS_OUTPUTS_SCRIPTPUBKEYS, 0, number | selall], r(..)), + (&[all, TXFS_OUTPUTS_VALUES, 0, number | selall], r(..)), + (&[all, TXFS_OUTPUTS_ALL, 0, number | selall], r(..)), + // inputs + (&[all, 0, number | selnone], r(..)), + (&[all, TXFS_INPUTS_PREVOUTS, selcur], r(..)), + (&[all, TXFS_INPUTS_SEQUENCES, selcur], r(..)), + (&[all, TXFS_INPUTS_SCRIPTSIGS, selcur], r(..)), + (&[all, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, selcur], r(..)), + (&[all, TXFS_INPUTS_PREV_VALUES, selcur], r(..)), + (&[all, TXFS_INPUTS_TAPROOT_ANNEXES, selcur], r(..)), + (&[all, TXFS_INPUTS_ALL, selcur], r(..)), + (&[all, TXFS_INPUTS_PREVOUTS, selall], r(..)), + (&[all, TXFS_INPUTS_SEQUENCES, selall], r(..)), + (&[all, TXFS_INPUTS_SCRIPTSIGS, selall], r(..)), + (&[all, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, selall], r(..)), + (&[all, TXFS_INPUTS_PREV_VALUES, selall], r(..)), + (&[all, TXFS_INPUTS_TAPROOT_ANNEXES, selall], r(..)), + (&[all, TXFS_INPUTS_ALL, selall], r(..)), + (&[all, TXFS_INPUTS_PREVOUTS, number | selcur], r(..)), + (&[all, TXFS_INPUTS_SEQUENCES, number | selcur], r(..)), + (&[all, TXFS_INPUTS_SCRIPTSIGS, number | selcur], r(..)), + (&[all, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, number | selcur], r(..)), + (&[all, TXFS_INPUTS_PREV_VALUES, number | selcur], r(..)), + (&[all, TXFS_INPUTS_TAPROOT_ANNEXES, number | selcur], r(..)), + (&[all, TXFS_INPUTS_ALL, number | selcur], r(..)), + (&[all, TXFS_INPUTS_PREVOUTS, number | selall], r(..)), + (&[all, TXFS_INPUTS_SEQUENCES, number | selall], r(..)), + (&[all, TXFS_INPUTS_SCRIPTSIGS, number | selall], r(..)), + (&[all, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, number | selall], r(..)), + (&[all, TXFS_INPUTS_PREV_VALUES, number | selall], r(..)), + (&[all, TXFS_INPUTS_TAPROOT_ANNEXES, number | selall], r(..)), + (&[all, TXFS_INPUTS_ALL, number | selall], r(..)), + // both + (&[all, allio, selall, selall], r(..)), + (&[all, allio, selcur, selcur], r(..nbout)), + (&[all, 0, number | selnone, number | selnone], r(..)), + (&[all, allio, number | selall, number | selall], r(..)), + (&[all, allio, number | selcur, number | selcur], r(..nbout)), + (&[all, allio, selcur, selall], r(..)), + (&[all, allio, selall, selcur], r(..nbout)), + // leading + (&[all, allio, leading | 0x01, number | leading | 0x02], r(..)), + (&[all, allio, number | selcur, leading | 0x02], r(..nbout)), + // individual absolute + (&[all, allio, individual | absolute | 0x01, 0x01, + individual | absolute | 0x02, 0x00, 0x02], r(..)), + (&[all, allio, number | individual | absolute | 0x01, 0x01, + number | individual | absolute | 0x02, 0x00, 0x02], r(..)), + // individual relative + (&[all, allio, individual | relative | 0x01, (-1i8 as u8) >> 1, + individual | relative | 0x02, (-1i8 as u8) >> 1, 0], r(1..nbout)), + (&[all, allio, number | individual | relative | 0x01, (-1i8 as u8) >> 1, + number | individual | relative | 0x02, (-1i8 as u8) >> 1, 0], r(1..nbout)), + //TODO(stevenroose) test index size, but for that we need > 32 in/outputs + // special case template + (&[], r(..)), + // shorthand txfs, sighash examples + (&[0b11111111], r(..)), + (&[0b11110111], r(..nbout)), + (&[0b11110011], r(..)), + (&[0b11111101], r(..)), + (&[0b11110101], r(..nbout)), + (&[0b11110001], r(..)), + (&[0b11101101], r(..)), + (&[0b11100101], r(..nbout)), + (&[0b11100001], r(..)), + (&[0b11001101], r(..)), + (&[0b11000101], r(..nbout)), + (&[0b11000001], r(..)), + ]; + + let cases = vec![ + test_vector_tx(), + ]; + + fn check_range(r: &Box, idx: usize) -> bool { + if let Some(ref range) = r.downcast_ref::() { + return range.contains(&idx); + } + if let Some(ref range) = r.downcast_ref::>() { + return range.contains(&idx); + } + if let Some(ref range) = r.downcast_ref::>() { + return range.contains(&idx); + } + if let Some(ref range) = r.downcast_ref::>() { + return range.contains(&idx); + } + unreachable!("invalid range type used: {:?}", r.type_id()); + } + + cases.into_iter().enumerate().map(|(cidx, (tx, prevs))| { + let mut vectors = Vec::new(); + for (_sidx, (txfs, idx_range)) in selectors.iter().enumerate() { + for i in 0..tx.input.len() { + // println!("{} >> #{} ({}) >> {}", cidx, _sidx, txfs.as_hex(), i); + if !check_range(idx_range, i) { + continue; + } + + match calculate_txhash(txfs, &tx, &prevs, i as u32, None) { + Ok(txhash) => vectors.push(TestVector { + txfs: txfs.to_vec(), + input: i, + codeseparator: None, + txhash: txhash, + }), + Err(e) => panic!("Error in vector #{} for selector {}: {}", + cidx, txfs.as_hex(), e, + ), + } + } + } + TestCase { tx, prevs, vectors } + }).collect() + } + + pub fn write_vector_file(path: impl AsRef) { + use bitcoin::consensus::encode::serialize_hex; + + let ret = generate_vectors().into_iter().enumerate().map(|(i_tx, c)| serde_json::json!({ + "tx": serialize_hex(&c.tx), + "prevs": c.prevs.iter().map(|p| serialize_hex(p)).collect::>(), + "vectors": c.vectors.into_iter().enumerate().map(|(i_v, v)| serde_json::json!({ + "id": format!("{}:{} ({} #{})", i_tx, i_v, v.txfs.as_hex(), v.input), + "txfs": v.txfs.as_hex().to_string(), + "input": v.input, + "codeseparator": v.codeseparator, + "txhash": v.txhash, + })).collect::>(), + })).collect::>(); + + let mut file = std::fs::File::create(path).unwrap(); + serde_json::to_writer_pretty(&mut file, &ret).unwrap(); + } +} + +fn main() { + test_vectors::write_vector_file("./txhash_vectors.json"); +} diff --git a/bip-0346/ref-impl/txhash_vectors.json b/bip-0346/ref-impl/txhash_vectors.json new file mode 100644 index 0000000000..0cf5a54ccc --- /dev/null +++ b/bip-0346/ref-impl/txhash_vectors.json @@ -0,0 +1,2015 @@ +[ + { + "prevs": [ + "68010000000000000160", + "69010000000000002200200100000000000000000000000000000000000000000000000000000000000000", + "69010000000000002251200100000000000000000000000000000000000000000000000000000000000000", + "6a010000000000002251200200000000000000000000000000000000000000000000000000000000000000" + ], + "tx": "02000000000104111111111111111111111111111111111111111111111111111111111111111101000000012301000000222222222222222222222222222222222222222222222222222222222222222202000000000300000033333333333333333333333333333333333333333333333333333333333333330300000000020000004444444444444444444444444444444444444444444444444444444444444444040000000003000000035e0100000000000001565f0100000000000001576101000000000000015800030113011402504201011203011301140250422a000000", + "vectors": [ + { + "codeseparator": null, + "id": "0:0 (01000000 #0)", + "input": 0, + "txfs": "01000000", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "id": "0:1 (01000000 #1)", + "input": 1, + "txfs": "01000000", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "id": "0:2 (01000000 #2)", + "input": 2, + "txfs": "01000000", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "id": "0:3 (01000000 #3)", + "input": 3, + "txfs": "01000000", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "id": "0:4 (02000000 #0)", + "input": 0, + "txfs": "02000000", + "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" + }, + { + "codeseparator": null, + "id": "0:5 (02000000 #1)", + "input": 1, + "txfs": "02000000", + "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" + }, + { + "codeseparator": null, + "id": "0:6 (02000000 #2)", + "input": 2, + "txfs": "02000000", + "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" + }, + { + "codeseparator": null, + "id": "0:7 (02000000 #3)", + "input": 3, + "txfs": "02000000", + "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" + }, + { + "codeseparator": null, + "id": "0:8 (04000000 #0)", + "input": 0, + "txfs": "04000000", + "txhash": "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119" + }, + { + "codeseparator": null, + "id": "0:9 (04000000 #1)", + "input": 1, + "txfs": "04000000", + "txhash": "67abdd721024f0ff4e0b3f4c2fc13bc5bad42d0b7851d456d88d203d15aaa450" + }, + { + "codeseparator": null, + "id": "0:10 (04000000 #2)", + "input": 2, + "txfs": "04000000", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "id": "0:11 (04000000 #3)", + "input": 3, + "txfs": "04000000", + "txhash": "9d9f290527a6be626a8f5985b26e19b237b44872b03631811df4416fc1713178" + }, + { + "codeseparator": null, + "id": "0:12 (08000000 #0)", + "input": 0, + "txfs": "08000000", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "id": "0:13 (08000000 #1)", + "input": 1, + "txfs": "08000000", + "txhash": "227747766d19539b54f018e7ccfde16bd7c38ebbf5649357ecf67bdfb9755b5c" + }, + { + "codeseparator": null, + "id": "0:14 (08000000 #2)", + "input": 2, + "txfs": "08000000", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "id": "0:15 (08000000 #3)", + "input": 3, + "txfs": "08000000", + "txhash": "58b8e1205472ebed51a76303179ebf44554714af49ef1f78fb4c1a6a795aa3d7" + }, + { + "codeseparator": null, + "id": "0:16 (10000000 #0)", + "input": 0, + "txfs": "10000000", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "id": "0:17 (10000000 #1)", + "input": 1, + "txfs": "10000000", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "id": "0:18 (10000000 #2)", + "input": 2, + "txfs": "10000000", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "id": "0:19 (10000000 #3)", + "input": 3, + "txfs": "10000000", + "txhash": "d703d3da6a87bd8e0b453f3b6c41edcc9bf331b2b88ef26eb39dc7abee4e00a3" + }, + { + "codeseparator": null, + "id": "0:20 (20000000 #0)", + "input": 0, + "txfs": "20000000", + "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + }, + { + "codeseparator": null, + "id": "0:21 (20000000 #1)", + "input": 1, + "txfs": "20000000", + "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + }, + { + "codeseparator": null, + "id": "0:22 (20000000 #2)", + "input": 2, + "txfs": "20000000", + "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + }, + { + "codeseparator": null, + "id": "0:23 (20000000 #3)", + "input": 3, + "txfs": "20000000", + "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + }, + { + "codeseparator": null, + "id": "0:24 (40000000 #0)", + "input": 0, + "txfs": "40000000", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "id": "0:25 (40000000 #1)", + "input": 1, + "txfs": "40000000", + "txhash": "227747766d19539b54f018e7ccfde16bd7c38ebbf5649357ecf67bdfb9755b5c" + }, + { + "codeseparator": null, + "id": "0:26 (40000000 #2)", + "input": 2, + "txfs": "40000000", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "id": "0:27 (40000000 #3)", + "input": 3, + "txfs": "40000000", + "txhash": "227747766d19539b54f018e7ccfde16bd7c38ebbf5649357ecf67bdfb9755b5c" + }, + { + "codeseparator": null, + "id": "0:28 (80000000 #0)", + "input": 0, + "txfs": "80000000", + "txhash": "50c8ba3a6170f0a2fb6736ece8a603576ef6309a35e810911599bc6211b554a9" + }, + { + "codeseparator": null, + "id": "0:29 (80000000 #1)", + "input": 1, + "txfs": "80000000", + "txhash": "50c8ba3a6170f0a2fb6736ece8a603576ef6309a35e810911599bc6211b554a9" + }, + { + "codeseparator": null, + "id": "0:30 (80000000 #2)", + "input": 2, + "txfs": "80000000", + "txhash": "50c8ba3a6170f0a2fb6736ece8a603576ef6309a35e810911599bc6211b554a9" + }, + { + "codeseparator": null, + "id": "0:31 (80000000 #3)", + "input": 3, + "txfs": "80000000", + "txhash": "50c8ba3a6170f0a2fb6736ece8a603576ef6309a35e810911599bc6211b554a9" + }, + { + "codeseparator": null, + "id": "0:32 (ff000080 #0)", + "input": 0, + "txfs": "ff000080", + "txhash": "6286469e7f9ac95ff97d41ab1cf8a1d8a3872e45f5bac80a1acf8fd9d4397c18" + }, + { + "codeseparator": null, + "id": "0:33 (ff000080 #1)", + "input": 1, + "txfs": "ff000080", + "txhash": "b2e78152d3e2d0e35929aaedc5082cf878fd14c02ac2a5635d0913e55957fbfb" + }, + { + "codeseparator": null, + "id": "0:34 (ff000080 #2)", + "input": 2, + "txfs": "ff000080", + "txhash": "89bddc408a262f9bf8434d6930cd32dfca8b827b2b28ee4777047c6e5401ed03" + }, + { + "codeseparator": null, + "id": "0:35 (ff000080 #3)", + "input": 3, + "txfs": "ff000080", + "txhash": "a6db50688a424eb51be31d7714d30ce440eeaf2affc394e9f191b1a85c451ac3" + }, + { + "codeseparator": null, + "id": "0:36 (ff400040 #0)", + "input": 0, + "txfs": "ff400040", + "txhash": "66c3a2d99e0eccce1e769d61b879f6003252912982a9efc138b24365d6c68879" + }, + { + "codeseparator": null, + "id": "0:37 (ff400040 #1)", + "input": 1, + "txfs": "ff400040", + "txhash": "8418c7e565ee4cb8146612c091320c4108f044173532b2e857d4e0428e2134cc" + }, + { + "codeseparator": null, + "id": "0:38 (ff400040 #2)", + "input": 2, + "txfs": "ff400040", + "txhash": "d6219143275e27cdfbdc67c2ffb5450c9a550e55bf31dfa8f3e63be3a0f121cf" + }, + { + "codeseparator": null, + "id": "0:39 (ff800040 #0)", + "input": 0, + "txfs": "ff800040", + "txhash": "52bc63b1b02c3d375d13236938b25d5249df08fb4d891686a70e5702ea12c0e4" + }, + { + "codeseparator": null, + "id": "0:40 (ff800040 #1)", + "input": 1, + "txfs": "ff800040", + "txhash": "3c433d439bc5e8fdfc6e3c9e711abb81d367b0d888aa68d3edfbac642a09241d" + }, + { + "codeseparator": null, + "id": "0:41 (ff800040 #2)", + "input": 2, + "txfs": "ff800040", + "txhash": "6542015a39b2032a637f3dfacad4a8d7d2c208ca6bf4208cd957f967a70eb6ae" + }, + { + "codeseparator": null, + "id": "0:42 (ffc00040 #0)", + "input": 0, + "txfs": "ffc00040", + "txhash": "75ae3a008d82c09aa7afd34abd46f03afe940758d33763b188d1fa2bd068cd80" + }, + { + "codeseparator": null, + "id": "0:43 (ffc00040 #1)", + "input": 1, + "txfs": "ffc00040", + "txhash": "4a45a68dd209b20cd3f7afbf7b99b18d2a2f6b65ea0365a61b1d96333ea6af32" + }, + { + "codeseparator": null, + "id": "0:44 (ffc00040 #2)", + "input": 2, + "txfs": "ffc00040", + "txhash": "e3fef168eb62944b7ff7b21e1518b4ccb436f710825aeea1905645fd53ecb949" + }, + { + "codeseparator": null, + "id": "0:45 (ff40003f #0)", + "input": 0, + "txfs": "ff40003f", + "txhash": "8dea16353d8dbb04764bc5a21fa36236aa3f736761cbb9be809a0a870fa6a366" + }, + { + "codeseparator": null, + "id": "0:46 (ff40003f #1)", + "input": 1, + "txfs": "ff40003f", + "txhash": "8fee6a452061370faa11f4ab4bb4e13adb4c756a4fdfbf2376d5c857716b9f41" + }, + { + "codeseparator": null, + "id": "0:47 (ff40003f #2)", + "input": 2, + "txfs": "ff40003f", + "txhash": "1c0f0f6709b8bf9e665772850386a61f30d776fd900560c8add6af860c4d57cc" + }, + { + "codeseparator": null, + "id": "0:48 (ff40003f #3)", + "input": 3, + "txfs": "ff40003f", + "txhash": "28b423da0e330852f9c1658b87dd4b26926b3a4c5465e2696b0b41a8e024c196" + }, + { + "codeseparator": null, + "id": "0:49 (ff80003f #0)", + "input": 0, + "txfs": "ff80003f", + "txhash": "d0b05b4c01125da54d473da6d5f21bef264cfa236e9dde70ff140027cef94931" + }, + { + "codeseparator": null, + "id": "0:50 (ff80003f #1)", + "input": 1, + "txfs": "ff80003f", + "txhash": "c684f7c0cfbb5c7ad5a60b2109aa914016b0809dd9fde032677085d061c2085a" + }, + { + "codeseparator": null, + "id": "0:51 (ff80003f #2)", + "input": 2, + "txfs": "ff80003f", + "txhash": "67e5312c5b589c832b56276d934f564b012f2f2559ac0e3bfa577694eed0a92d" + }, + { + "codeseparator": null, + "id": "0:52 (ff80003f #3)", + "input": 3, + "txfs": "ff80003f", + "txhash": "5a59141f7a9fbf4f48f4ce07dd7ee24490ee98a05ee87c948009305283d91ed9" + }, + { + "codeseparator": null, + "id": "0:53 (ffc0003f #0)", + "input": 0, + "txfs": "ffc0003f", + "txhash": "a28ff0cd1109352b4dcbf74a0d2a6c73732235b532bb8284ad388b8e49838ea5" + }, + { + "codeseparator": null, + "id": "0:54 (ffc0003f #1)", + "input": 1, + "txfs": "ffc0003f", + "txhash": "55cc87e4057e6ea5a38f03a86c3ab22c783c801a42a158531e1048be8a78ed0a" + }, + { + "codeseparator": null, + "id": "0:55 (ffc0003f #2)", + "input": 2, + "txfs": "ffc0003f", + "txhash": "9420a19552950ee67de25a4ef4bf07f53bcadbd833069a06d2f21a9179a9a359" + }, + { + "codeseparator": null, + "id": "0:56 (ffc0003f #3)", + "input": 3, + "txfs": "ffc0003f", + "txhash": "c99d71c62a55ecc52a2f113588dbc031ec00b78983af60700f56ac0ed05ae251" + }, + { + "codeseparator": null, + "id": "0:57 (ff4000c0 #0)", + "input": 0, + "txfs": "ff4000c0", + "txhash": "af6e1adf1ff16c7bae08e4127b30dab4fedee683277c8b65e27897e9013d63cf" + }, + { + "codeseparator": null, + "id": "0:58 (ff4000c0 #1)", + "input": 1, + "txfs": "ff4000c0", + "txhash": "f3d20d6fce0869617c5c52617f429696dd78cad648077a6d468c01f152ab1d38" + }, + { + "codeseparator": null, + "id": "0:59 (ff4000c0 #2)", + "input": 2, + "txfs": "ff4000c0", + "txhash": "c6bc80c9588714990891569b8a948417c75202800aba176b1fcfd50d2e066dc8" + }, + { + "codeseparator": null, + "id": "0:60 (ff8000c0 #0)", + "input": 0, + "txfs": "ff8000c0", + "txhash": "8ce6884e8b3a11d81f69b9642981770cf2f7ed59cd7748e66f443e10cb16094d" + }, + { + "codeseparator": null, + "id": "0:61 (ff8000c0 #1)", + "input": 1, + "txfs": "ff8000c0", + "txhash": "75f9fd03b70f4366dfd52e4cafc3441b28b98fbcb6553fc25c4f6306feb552c0" + }, + { + "codeseparator": null, + "id": "0:62 (ff8000c0 #2)", + "input": 2, + "txfs": "ff8000c0", + "txhash": "9402d41fc7af5607bbf94315ef8f9380e74af3681bb8cf26861a85071401f8b7" + }, + { + "codeseparator": null, + "id": "0:63 (ffc000c0 #0)", + "input": 0, + "txfs": "ffc000c0", + "txhash": "29c5f8ace77d67bc684be308d8bba076ddbccb7db86890f1affde88243111d66" + }, + { + "codeseparator": null, + "id": "0:64 (ffc000c0 #1)", + "input": 1, + "txfs": "ffc000c0", + "txhash": "4d3256647fd0d527b3e39babc2ba24aaff442d6d2170ac4b9737bd9665e9071a" + }, + { + "codeseparator": null, + "id": "0:65 (ffc000c0 #2)", + "input": 2, + "txfs": "ffc000c0", + "txhash": "daf30b66d9952f6bc5a35aab123d85c8a3b6e647532f84280b0d3d71bd52ec77" + }, + { + "codeseparator": null, + "id": "0:66 (ff4000bf #0)", + "input": 0, + "txfs": "ff4000bf", + "txhash": "a111bf0e5c46831243ec5d8095ffdb959b316c00e00296dfce12b5dc817f1d29" + }, + { + "codeseparator": null, + "id": "0:67 (ff4000bf #1)", + "input": 1, + "txfs": "ff4000bf", + "txhash": "6f3b3a29c97690155ec01716ea28ada8d3183c29661478a6431eaef02a9f58cc" + }, + { + "codeseparator": null, + "id": "0:68 (ff4000bf #2)", + "input": 2, + "txfs": "ff4000bf", + "txhash": "4a1d3b70e8eb909b6f03c5bd6fe190cdbb8a48efc0c447fb998b2cd78ae20182" + }, + { + "codeseparator": null, + "id": "0:69 (ff4000bf #3)", + "input": 3, + "txfs": "ff4000bf", + "txhash": "42b91289081bb83ba89adfecc242330d5e8d249e851c87b1d48ba8e17d9d030e" + }, + { + "codeseparator": null, + "id": "0:70 (ff8000bf #0)", + "input": 0, + "txfs": "ff8000bf", + "txhash": "2ecd12402dd1443629037d5864d5464d9c8812bd44449bfc25b16b0c76bfff79" + }, + { + "codeseparator": null, + "id": "0:71 (ff8000bf #1)", + "input": 1, + "txfs": "ff8000bf", + "txhash": "474d9aa092d174230da146cc13818d3c9c7b1c96da718170aad5de1af4b9e079" + }, + { + "codeseparator": null, + "id": "0:72 (ff8000bf #2)", + "input": 2, + "txfs": "ff8000bf", + "txhash": "ab7911290b663cc48ea387e1c48bc9b0e2113786b79d57d86456e656726c7ad1" + }, + { + "codeseparator": null, + "id": "0:73 (ff8000bf #3)", + "input": 3, + "txfs": "ff8000bf", + "txhash": "377d323d69c73477ad361f2a07d5861ea4993d2bc9803dc3846ec05394fac8d4" + }, + { + "codeseparator": null, + "id": "0:74 (ffc000bf #0)", + "input": 0, + "txfs": "ffc000bf", + "txhash": "6b7d5990b3435e42a4212c88967eb0f9c8b3c3738ecc2fc79d4b48bb386f178c" + }, + { + "codeseparator": null, + "id": "0:75 (ffc000bf #1)", + "input": 1, + "txfs": "ffc000bf", + "txhash": "285857c9816c024286955be8ceddef9fb6f56b47124ebe8b1857fa045081697f" + }, + { + "codeseparator": null, + "id": "0:76 (ffc000bf #2)", + "input": 2, + "txfs": "ffc000bf", + "txhash": "a7bf9d7a948832fa48d26a1283690eccf1e02e38359ac8241f71751b6a259a6c" + }, + { + "codeseparator": null, + "id": "0:77 (ffc000bf #3)", + "input": 3, + "txfs": "ffc000bf", + "txhash": "3238757d33922c6e3707efe96f8973d0857c8ac45c8bfee5beb78c663eef123c" + }, + { + "codeseparator": null, + "id": "0:78 (ff0080 #0)", + "input": 0, + "txfs": "ff0080", + "txhash": "3a23df77318fd630a1b004bd49ee23e2f6ffb588a3e38d50577a307401f67127" + }, + { + "codeseparator": null, + "id": "0:79 (ff0080 #1)", + "input": 1, + "txfs": "ff0080", + "txhash": "e043c75b8a88136961cbff8d9071793793990e96b05b495249baa455b78979cc" + }, + { + "codeseparator": null, + "id": "0:80 (ff0080 #2)", + "input": 2, + "txfs": "ff0080", + "txhash": "0d3ceeaf969a67f3068c0906fed9b6908984f5fb50f46146c7f337f7da6d2d05" + }, + { + "codeseparator": null, + "id": "0:81 (ff0080 #3)", + "input": 3, + "txfs": "ff0080", + "txhash": "83911d80f67db127ae54d2134bc2d90940fa77d5b3cd4c611be155bde87eb0e0" + }, + { + "codeseparator": null, + "id": "0:82 (ff0140 #0)", + "input": 0, + "txfs": "ff0140", + "txhash": "90b539676860d03f2fffd6c6ae1538528348151246c67204cd3f411237e5b15d" + }, + { + "codeseparator": null, + "id": "0:83 (ff0140 #1)", + "input": 1, + "txfs": "ff0140", + "txhash": "724dbfef003b8649c208395acdc4bf6e4057a7e79ec86c0e4c2b4f120efce189" + }, + { + "codeseparator": null, + "id": "0:84 (ff0140 #2)", + "input": 2, + "txfs": "ff0140", + "txhash": "9bbcd1ee9f364381d65886fd28c7aaac3c9ab599dda7610f482ca6e27d30f19d" + }, + { + "codeseparator": null, + "id": "0:85 (ff0140 #3)", + "input": 3, + "txfs": "ff0140", + "txhash": "5b866d2d935f361426397fb15d80cf1c88c9e7bcae8175ecf1d47d2623053d2c" + }, + { + "codeseparator": null, + "id": "0:86 (ff0240 #0)", + "input": 0, + "txfs": "ff0240", + "txhash": "a784cde6d6ed5beefe6677cd7701a14973dc852346cdb3086153f4bd7f23e4c3" + }, + { + "codeseparator": null, + "id": "0:87 (ff0240 #1)", + "input": 1, + "txfs": "ff0240", + "txhash": "a327e4727d2dedd51189d31a7df7bab17bf87bbf19c0fc33e51b0b9ac3cd09de" + }, + { + "codeseparator": null, + "id": "0:88 (ff0240 #2)", + "input": 2, + "txfs": "ff0240", + "txhash": "dbd0495acb402ca59b1999bb692fcf4e486299e5675c89ab7c7892a2962b1caa" + }, + { + "codeseparator": null, + "id": "0:89 (ff0240 #3)", + "input": 3, + "txfs": "ff0240", + "txhash": "85e80d26b7bbd44b36fb50bcd69bddaad41f9ddb0090beeaf4c608fa21bc3049" + }, + { + "codeseparator": null, + "id": "0:90 (ff0440 #0)", + "input": 0, + "txfs": "ff0440", + "txhash": "183347a01e8edf7c8a4b24053167973b5131012d6a2bc39fdc69e9fa794c7a80" + }, + { + "codeseparator": null, + "id": "0:91 (ff0440 #1)", + "input": 1, + "txfs": "ff0440", + "txhash": "9847b1e185d1b2297fe92c0c2ba719aa429a5ba17b53b725dec5a09105331e57" + }, + { + "codeseparator": null, + "id": "0:92 (ff0440 #2)", + "input": 2, + "txfs": "ff0440", + "txhash": "704d0278f5c2fd08f38a013980d6c975ff97e694215690fbfa7956fda71186b6" + }, + { + "codeseparator": null, + "id": "0:93 (ff0440 #3)", + "input": 3, + "txfs": "ff0440", + "txhash": "37beaf94bb9d6ce6de329f38e1a05df672735b48d463298a84e7d5f5565717f7" + }, + { + "codeseparator": null, + "id": "0:94 (ff0840 #0)", + "input": 0, + "txfs": "ff0840", + "txhash": "ca8f34c08d12c09c8f9a9b5558a630838899f11a11c654f9af3366db2362b473" + }, + { + "codeseparator": null, + "id": "0:95 (ff0840 #1)", + "input": 1, + "txfs": "ff0840", + "txhash": "b0e577c612f372b96b421d26832542fa004d0868a4bb76ebf139db58a40e57d1" + }, + { + "codeseparator": null, + "id": "0:96 (ff0840 #2)", + "input": 2, + "txfs": "ff0840", + "txhash": "fd8e80b8d4b3c837595bf23b3cbcad6039f8ea0edcd4307be0753f53f5aa9f3a" + }, + { + "codeseparator": null, + "id": "0:97 (ff0840 #3)", + "input": 3, + "txfs": "ff0840", + "txhash": "64dd04e9f8b99a3ec51efb92d8deac3d267c386441306ce6053d307d7ee10422" + }, + { + "codeseparator": null, + "id": "0:98 (ff1040 #0)", + "input": 0, + "txfs": "ff1040", + "txhash": "4de8baed7a3742a6b16d2cee2dc3375728379ebb3af3c5ef77d9295439959913" + }, + { + "codeseparator": null, + "id": "0:99 (ff1040 #1)", + "input": 1, + "txfs": "ff1040", + "txhash": "6b2231cdac56a0d3723348c7ab9baa73692e163a3cb24728db46726376e8b597" + }, + { + "codeseparator": null, + "id": "0:100 (ff1040 #2)", + "input": 2, + "txfs": "ff1040", + "txhash": "134216d1e5bf59df526813a3161794d63c414da3c036e628fd84e976db23d1a4" + }, + { + "codeseparator": null, + "id": "0:101 (ff1040 #3)", + "input": 3, + "txfs": "ff1040", + "txhash": "8dbfde5d0105a4e954e08c77139250f83ec3eb764b17906a9219d20933dab5dc" + }, + { + "codeseparator": null, + "id": "0:102 (ff2040 #0)", + "input": 0, + "txfs": "ff2040", + "txhash": "1a7a2897721b14ebabac2dd83964323864d6f54610a21c255b7a61be4042f269" + }, + { + "codeseparator": null, + "id": "0:103 (ff2040 #1)", + "input": 1, + "txfs": "ff2040", + "txhash": "da6e2c714c87ba4f35c517bdda9e040b2e32b0eed32229c3ec8fb25d9da79689" + }, + { + "codeseparator": null, + "id": "0:104 (ff2040 #2)", + "input": 2, + "txfs": "ff2040", + "txhash": "c8037e3ffbd1cf6a6b772a692342134f5996e2cce8904379d75204edb7aa5c74" + }, + { + "codeseparator": null, + "id": "0:105 (ff2040 #3)", + "input": 3, + "txfs": "ff2040", + "txhash": "4010809d721f277e8f5d4acbf4b51966fcf5c634b4c8d0212f7d95c0f77080c9" + }, + { + "codeseparator": null, + "id": "0:106 (ff3f40 #0)", + "input": 0, + "txfs": "ff3f40", + "txhash": "a6ba282e76f21cdf1e459c8de72273a9942c569fdb1fc71929c58aa6b07c4c51" + }, + { + "codeseparator": null, + "id": "0:107 (ff3f40 #1)", + "input": 1, + "txfs": "ff3f40", + "txhash": "95af6b38eda1f29d93074e44595cd0ed3c0d995cadddaa9f0c38d886163ea8d8" + }, + { + "codeseparator": null, + "id": "0:108 (ff3f40 #2)", + "input": 2, + "txfs": "ff3f40", + "txhash": "7f92417c956f7e043844c336b83df0b66ee581c7f29e94face75e4072246ab86" + }, + { + "codeseparator": null, + "id": "0:109 (ff3f40 #3)", + "input": 3, + "txfs": "ff3f40", + "txhash": "c910c1cf46cb1291d2e4909d972fa506d987015dc65d8392057480fc2c000b53" + }, + { + "codeseparator": null, + "id": "0:110 (ff013f #0)", + "input": 0, + "txfs": "ff013f", + "txhash": "fa53c1142eff69b0475edba6a41e1d56f98f3cc556070408b3590029fe9c75fa" + }, + { + "codeseparator": null, + "id": "0:111 (ff013f #1)", + "input": 1, + "txfs": "ff013f", + "txhash": "0c3d38d991eaf8898ae9828f5bab3d32a11f2b98b7881dcfb086967762043cab" + }, + { + "codeseparator": null, + "id": "0:112 (ff013f #2)", + "input": 2, + "txfs": "ff013f", + "txhash": "d868fba5ceaf5d16dd8467c23f75744ec09d81d46d888857372cc03055346c9f" + }, + { + "codeseparator": null, + "id": "0:113 (ff013f #3)", + "input": 3, + "txfs": "ff013f", + "txhash": "113cbcd00db595984727cfd3c75bb0cf41ca9a64d5a58cdaa77abcd1e9cfab7a" + }, + { + "codeseparator": null, + "id": "0:114 (ff023f #0)", + "input": 0, + "txfs": "ff023f", + "txhash": "b8773842127b6c975b85c41cef1ddd5b64ec09de07810784f53a3d6279722979" + }, + { + "codeseparator": null, + "id": "0:115 (ff023f #1)", + "input": 1, + "txfs": "ff023f", + "txhash": "c3b366f02ec6eecfce439884326831c5ee0f551a288cfc58f6cf23d40919f6c6" + }, + { + "codeseparator": null, + "id": "0:116 (ff023f #2)", + "input": 2, + "txfs": "ff023f", + "txhash": "8dfda435a0cf203345e33dd6fff7308565fe9486373dd54ce0ed8782f3006b0a" + }, + { + "codeseparator": null, + "id": "0:117 (ff023f #3)", + "input": 3, + "txfs": "ff023f", + "txhash": "13467381f9e10a56dfc664407f02485d7c802a0d738cd521593a18b8434192f9" + }, + { + "codeseparator": null, + "id": "0:118 (ff043f #0)", + "input": 0, + "txfs": "ff043f", + "txhash": "0d7408c4e812a5e730f5e7803473d7fe809d82734f0760ecfacfa2dc0b23ded2" + }, + { + "codeseparator": null, + "id": "0:119 (ff043f #1)", + "input": 1, + "txfs": "ff043f", + "txhash": "48b3f61a534b46593257de21463060a80e390fb852e764ea67f869fc1cf8e15e" + }, + { + "codeseparator": null, + "id": "0:120 (ff043f #2)", + "input": 2, + "txfs": "ff043f", + "txhash": "1c4e885088cb9f9726dd03d18a77948e4bb638d24ac31eba9c736e373a28ee7d" + }, + { + "codeseparator": null, + "id": "0:121 (ff043f #3)", + "input": 3, + "txfs": "ff043f", + "txhash": "2203b7708ece88e944d27080b275d290664f20e9ef5810fe784b1e9dc5694106" + }, + { + "codeseparator": null, + "id": "0:122 (ff083f #0)", + "input": 0, + "txfs": "ff083f", + "txhash": "88c494e1486266603caa72cdbbe564a0c560a6f7e9b6d8833005ea0116884649" + }, + { + "codeseparator": null, + "id": "0:123 (ff083f #1)", + "input": 1, + "txfs": "ff083f", + "txhash": "2cdf1a588d129dfb1c7121009ded3630aad62caace81e1439b3de2bdbbaea645" + }, + { + "codeseparator": null, + "id": "0:124 (ff083f #2)", + "input": 2, + "txfs": "ff083f", + "txhash": "bc75169b06efa7c59ec611d6fc3d2ea6c703fb8364aefdebd609db08c8bae80b" + }, + { + "codeseparator": null, + "id": "0:125 (ff083f #3)", + "input": 3, + "txfs": "ff083f", + "txhash": "86634079c6520e2681739b15b9e57fcd6727642e72b03b3f4b4f64fb647f1331" + }, + { + "codeseparator": null, + "id": "0:126 (ff103f #0)", + "input": 0, + "txfs": "ff103f", + "txhash": "875f22fd4b5cdd53a44e677a11c40cff8fb18bcd50d2aa9c6bd114ae96601574" + }, + { + "codeseparator": null, + "id": "0:127 (ff103f #1)", + "input": 1, + "txfs": "ff103f", + "txhash": "d333cc2abf1abeecb29415168be08686bd57559afb06d30e636e1a65ac3fbcea" + }, + { + "codeseparator": null, + "id": "0:128 (ff103f #2)", + "input": 2, + "txfs": "ff103f", + "txhash": "5947a4ac6fab4d1bfc529414732f202017177c8c9e66e8575b1289fba8457e07" + }, + { + "codeseparator": null, + "id": "0:129 (ff103f #3)", + "input": 3, + "txfs": "ff103f", + "txhash": "1097449a62608581ef7b9aede9d2cbd6c15c5fbdbf1258835046ca67833fe9cd" + }, + { + "codeseparator": null, + "id": "0:130 (ff203f #0)", + "input": 0, + "txfs": "ff203f", + "txhash": "f988870197924578a2adfb24c49959970720ed5bda540e8bfefd2daa2f2d555d" + }, + { + "codeseparator": null, + "id": "0:131 (ff203f #1)", + "input": 1, + "txfs": "ff203f", + "txhash": "c434a9471bb08f90dc137f2b913bdd5d3ab3b434a9a18ae632313c49ccd501ca" + }, + { + "codeseparator": null, + "id": "0:132 (ff203f #2)", + "input": 2, + "txfs": "ff203f", + "txhash": "e1eeea3d57cd7ad86f74c63f4ca7e536bd5b2a502130c057685996a565a6d7c8" + }, + { + "codeseparator": null, + "id": "0:133 (ff203f #3)", + "input": 3, + "txfs": "ff203f", + "txhash": "dce9fbd981975598e42a6755cee9ec7807b09fdd07b29564c76381164b62ac4b" + }, + { + "codeseparator": null, + "id": "0:134 (ff3f3f #0)", + "input": 0, + "txfs": "ff3f3f", + "txhash": "e6aa7ef7e51a5787f6938c7644b7a5c84d0969b3f0907f9aebd4aa74f54931ee" + }, + { + "codeseparator": null, + "id": "0:135 (ff3f3f #1)", + "input": 1, + "txfs": "ff3f3f", + "txhash": "65b54a5c2d79ef2d1da7957a63effee3b26689098301a397c531ef39d728f0be" + }, + { + "codeseparator": null, + "id": "0:136 (ff3f3f #2)", + "input": 2, + "txfs": "ff3f3f", + "txhash": "83ed0451b7b803dc45f91543566ae70eea69f0f5eba49d6b470596f3954ccd75" + }, + { + "codeseparator": null, + "id": "0:137 (ff3f3f #3)", + "input": 3, + "txfs": "ff3f3f", + "txhash": "68018bfc33c1371a9821c6fd3077d5c602edfab802f12fb959c333b58cfaee5c" + }, + { + "codeseparator": null, + "id": "0:138 (ff01c0 #0)", + "input": 0, + "txfs": "ff01c0", + "txhash": "e65e4d551a952594a0ab7310f8ee9a25088a487945400a9e17aa5ad3ec999d60" + }, + { + "codeseparator": null, + "id": "0:139 (ff01c0 #1)", + "input": 1, + "txfs": "ff01c0", + "txhash": "9d6de430536dc2cd8c61f369841455dc98f4e18e03d124e07a68898f5a7666af" + }, + { + "codeseparator": null, + "id": "0:140 (ff01c0 #2)", + "input": 2, + "txfs": "ff01c0", + "txhash": "5e2ee09b6215d25205f720fcf4d70e2cf4e45039b0663db1663f6342adc9a906" + }, + { + "codeseparator": null, + "id": "0:141 (ff01c0 #3)", + "input": 3, + "txfs": "ff01c0", + "txhash": "d9762afaf50a660e9c96478a5fa74acb7ab3856b01487f9fd251e3c68744803e" + }, + { + "codeseparator": null, + "id": "0:142 (ff02c0 #0)", + "input": 0, + "txfs": "ff02c0", + "txhash": "64a2630ecb72742655161c85afe86372af17e2700268e9c7612cb0f212467b56" + }, + { + "codeseparator": null, + "id": "0:143 (ff02c0 #1)", + "input": 1, + "txfs": "ff02c0", + "txhash": "5f86d7b5b6cc5359bec91cdb4b0ee18793d69ea3820c5793cba49f3ec16ed50e" + }, + { + "codeseparator": null, + "id": "0:144 (ff02c0 #2)", + "input": 2, + "txfs": "ff02c0", + "txhash": "58bcf5c8b46378f3713fcd72964990c4f686b76f1baba23a293f815b3199b2db" + }, + { + "codeseparator": null, + "id": "0:145 (ff02c0 #3)", + "input": 3, + "txfs": "ff02c0", + "txhash": "1c5f54c4442ff0362ec4eca5612c9f8dd0b4043ce05959f72fc7bac6d158ffc3" + }, + { + "codeseparator": null, + "id": "0:146 (ff04c0 #0)", + "input": 0, + "txfs": "ff04c0", + "txhash": "80da9294b69201b82de4de0c5b5acd25b866a7803f02c8f90b95ed98806ce60d" + }, + { + "codeseparator": null, + "id": "0:147 (ff04c0 #1)", + "input": 1, + "txfs": "ff04c0", + "txhash": "fc3935120808b9d3d542cf5b63eba8aeedc26eac7b4fbcb16b9b4946b3559da6" + }, + { + "codeseparator": null, + "id": "0:148 (ff04c0 #2)", + "input": 2, + "txfs": "ff04c0", + "txhash": "f7d9c6e04506d50ed5a669ecde26a777b778149fab63cd21c2fb12235491db51" + }, + { + "codeseparator": null, + "id": "0:149 (ff04c0 #3)", + "input": 3, + "txfs": "ff04c0", + "txhash": "e6a8f6f208a0f117c97898cd858a197ba14d165ee13d6d24ec6b454bc161dfe8" + }, + { + "codeseparator": null, + "id": "0:150 (ff08c0 #0)", + "input": 0, + "txfs": "ff08c0", + "txhash": "5598a50d5e313a3b82239e195f60da91d117c29a2b52a686d310b2679c929ea1" + }, + { + "codeseparator": null, + "id": "0:151 (ff08c0 #1)", + "input": 1, + "txfs": "ff08c0", + "txhash": "2c569724d32b70003860d12980bbd544780df2cc9baa0428f19f49188841a37c" + }, + { + "codeseparator": null, + "id": "0:152 (ff08c0 #2)", + "input": 2, + "txfs": "ff08c0", + "txhash": "d7b47a842b27997c4ae65969804489ca41277498fde5556a092830a4de614b2b" + }, + { + "codeseparator": null, + "id": "0:153 (ff08c0 #3)", + "input": 3, + "txfs": "ff08c0", + "txhash": "0cb401ee5e07c1eaf8356054f285247fe9a3554600f05122a736cf60faef5688" + }, + { + "codeseparator": null, + "id": "0:154 (ff10c0 #0)", + "input": 0, + "txfs": "ff10c0", + "txhash": "e4760919677cec243f4315642acd49407aaca465a31d46307552fa55ce2e8583" + }, + { + "codeseparator": null, + "id": "0:155 (ff10c0 #1)", + "input": 1, + "txfs": "ff10c0", + "txhash": "952c0af1d6b7f715355b8e5ace305b77c32d211338cc073fe70915b11933fd9d" + }, + { + "codeseparator": null, + "id": "0:156 (ff10c0 #2)", + "input": 2, + "txfs": "ff10c0", + "txhash": "21678444eeadeaad66c896a5b0b81fb551914489d5180e3bd1e693e86bd0ea23" + }, + { + "codeseparator": null, + "id": "0:157 (ff10c0 #3)", + "input": 3, + "txfs": "ff10c0", + "txhash": "6629ba7be1f18c88f56b88fb04679d1b8fa539e1d70936efe3c778508ba132f2" + }, + { + "codeseparator": null, + "id": "0:158 (ff20c0 #0)", + "input": 0, + "txfs": "ff20c0", + "txhash": "70bf9abee31014fc3e454049535e115948ea16022323d58eda379801763b00c5" + }, + { + "codeseparator": null, + "id": "0:159 (ff20c0 #1)", + "input": 1, + "txfs": "ff20c0", + "txhash": "9f71d4ea926a6de5bcbab043f4de15b4ce5a13574d8c2e98df829fb470495e14" + }, + { + "codeseparator": null, + "id": "0:160 (ff20c0 #2)", + "input": 2, + "txfs": "ff20c0", + "txhash": "9ec5280ec8a4df11e4bd6aaa02d3ce966503d5be2965aa641db75047766657f5" + }, + { + "codeseparator": null, + "id": "0:161 (ff20c0 #3)", + "input": 3, + "txfs": "ff20c0", + "txhash": "04e13977f0cbec012dfac04b02fdbcd52625767001bf65b432dc1ed15b015d75" + }, + { + "codeseparator": null, + "id": "0:162 (ff3fc0 #0)", + "input": 0, + "txfs": "ff3fc0", + "txhash": "096199bf667fdcb4f34ed767bc914c23608cb2c99fb0e166183a59eac671262a" + }, + { + "codeseparator": null, + "id": "0:163 (ff3fc0 #1)", + "input": 1, + "txfs": "ff3fc0", + "txhash": "88d3556af7595d4d8e6181bfe5bb2fbbafbd1c10b4d0b9379c83dbdbd4b1aecd" + }, + { + "codeseparator": null, + "id": "0:164 (ff3fc0 #2)", + "input": 2, + "txfs": "ff3fc0", + "txhash": "d1dae4cbdfd915941cc0f983aea030e9b318aa543de4e622676516d56ee02a3b" + }, + { + "codeseparator": null, + "id": "0:165 (ff3fc0 #3)", + "input": 3, + "txfs": "ff3fc0", + "txhash": "1379c4fe2d9476d3f91efc1f9cf137e61b84889da1db23a59b3a121775f932ff" + }, + { + "codeseparator": null, + "id": "0:166 (ff01bf #0)", + "input": 0, + "txfs": "ff01bf", + "txhash": "be11834bc7776fe8bdde1787e644a5f1da32ea4d0df0c9ca232a141ebdb57b14" + }, + { + "codeseparator": null, + "id": "0:167 (ff01bf #1)", + "input": 1, + "txfs": "ff01bf", + "txhash": "4060c208bb3f1250e1eb84017ec0fd885c196b34fc0e5f4555fa99b1b8909be7" + }, + { + "codeseparator": null, + "id": "0:168 (ff01bf #2)", + "input": 2, + "txfs": "ff01bf", + "txhash": "5fba93f388205d57c274dee5f1a2b00cec7d549922a6769ec08da73c65836a6d" + }, + { + "codeseparator": null, + "id": "0:169 (ff01bf #3)", + "input": 3, + "txfs": "ff01bf", + "txhash": "2da6ce6c058a9b52dde76fd07f658f85b597015b4173b92231e7fff1ea1bcf6b" + }, + { + "codeseparator": null, + "id": "0:170 (ff02bf #0)", + "input": 0, + "txfs": "ff02bf", + "txhash": "08e6b11730294ced98f1ca24e5288c6b866919e2e818839a4788246786c4ac87" + }, + { + "codeseparator": null, + "id": "0:171 (ff02bf #1)", + "input": 1, + "txfs": "ff02bf", + "txhash": "b877ab5ab922d67a25f4d7d2bbaa671679af6513fc5e8fdac0cd8ec24d2bc005" + }, + { + "codeseparator": null, + "id": "0:172 (ff02bf #2)", + "input": 2, + "txfs": "ff02bf", + "txhash": "7807ab62904dd005b6c084c5bc372e4c5c702911ebbf360d3363e62cc53c94c7" + }, + { + "codeseparator": null, + "id": "0:173 (ff02bf #3)", + "input": 3, + "txfs": "ff02bf", + "txhash": "77abf6f63ee453ab284bac3a05384962b50e22e75c15ab604b95adc0c82d7092" + }, + { + "codeseparator": null, + "id": "0:174 (ff04bf #0)", + "input": 0, + "txfs": "ff04bf", + "txhash": "e4dfea896e91b2bf057b42eb0e8496ee18d396092559d087d2304a5cd5afe7ff" + }, + { + "codeseparator": null, + "id": "0:175 (ff04bf #1)", + "input": 1, + "txfs": "ff04bf", + "txhash": "60989de0bf2a4d3ea363b1a85be485b0b1d12157a36760e0ca9ae1402e19fcfc" + }, + { + "codeseparator": null, + "id": "0:176 (ff04bf #2)", + "input": 2, + "txfs": "ff04bf", + "txhash": "315ea3ff75fd849d61e88ecf017962782f75cfa28351c072950739361279c773" + }, + { + "codeseparator": null, + "id": "0:177 (ff04bf #3)", + "input": 3, + "txfs": "ff04bf", + "txhash": "08a8b32f4f60236be90abe4312aadfe54992a67565eeeacaf3c4fcb14bbce4be" + }, + { + "codeseparator": null, + "id": "0:178 (ff08bf #0)", + "input": 0, + "txfs": "ff08bf", + "txhash": "96e8acdc3509f491df8876a923656359fe90b5220788bfd081bb835889b415ec" + }, + { + "codeseparator": null, + "id": "0:179 (ff08bf #1)", + "input": 1, + "txfs": "ff08bf", + "txhash": "9bfc914dd5d2bda7123d2aefd99d2c8bdc985843eda3d51ba7f498936649ca34" + }, + { + "codeseparator": null, + "id": "0:180 (ff08bf #2)", + "input": 2, + "txfs": "ff08bf", + "txhash": "2bd7233870d4b67c6349de67c2d0b80259c6a7be77ab28d2b3504ce63f16d994" + }, + { + "codeseparator": null, + "id": "0:181 (ff08bf #3)", + "input": 3, + "txfs": "ff08bf", + "txhash": "103bccd96698e6abcfc09c959b8ce8d4bdc40fc324ff91ed9c7b464a69aa9df7" + }, + { + "codeseparator": null, + "id": "0:182 (ff10bf #0)", + "input": 0, + "txfs": "ff10bf", + "txhash": "89054cefa9119b41883e1e75771901a4f533c584cf3ff95ece5ade08e6004f71" + }, + { + "codeseparator": null, + "id": "0:183 (ff10bf #1)", + "input": 1, + "txfs": "ff10bf", + "txhash": "04b74338b1f54d406691e849c15a3a06ff540cfd8e9cf97bb640c88457bccc6a" + }, + { + "codeseparator": null, + "id": "0:184 (ff10bf #2)", + "input": 2, + "txfs": "ff10bf", + "txhash": "aca5e50ca02863f2c01fd7537ba2b3179c2d3c462aaed6448251e434fc4a85e1" + }, + { + "codeseparator": null, + "id": "0:185 (ff10bf #3)", + "input": 3, + "txfs": "ff10bf", + "txhash": "cd40afd9b65b426247dd69859094f13efd17a058dadc34369241be5236b6960e" + }, + { + "codeseparator": null, + "id": "0:186 (ff20bf #0)", + "input": 0, + "txfs": "ff20bf", + "txhash": "0513280c44b68ccdcb1e511a01dddef3379834400064301d65e2212dfb85c913" + }, + { + "codeseparator": null, + "id": "0:187 (ff20bf #1)", + "input": 1, + "txfs": "ff20bf", + "txhash": "3a7e6303ae22850670381004dfbe13d951f31157a9a59e7a28597289b0205e32" + }, + { + "codeseparator": null, + "id": "0:188 (ff20bf #2)", + "input": 2, + "txfs": "ff20bf", + "txhash": "252c38d12f2d6795318b2da1c8f0aac9a62ff9c69ff5e5e200380018e1c06c60" + }, + { + "codeseparator": null, + "id": "0:189 (ff20bf #3)", + "input": 3, + "txfs": "ff20bf", + "txhash": "b53a3338cd5c1acb3d8ac1ac8625fd534cfc471d2a70e94523a97080806104e5" + }, + { + "codeseparator": null, + "id": "0:190 (ff3fbf #0)", + "input": 0, + "txfs": "ff3fbf", + "txhash": "a8982b3cbe0249df3672cfc480d8e66e598844f043cf4d3736e30613d0dfce3d" + }, + { + "codeseparator": null, + "id": "0:191 (ff3fbf #1)", + "input": 1, + "txfs": "ff3fbf", + "txhash": "124c7d1fc8845e0f979e2e75f8503c16195bdcbdd086e0fffdae468eb0d2989e" + }, + { + "codeseparator": null, + "id": "0:192 (ff3fbf #2)", + "input": 2, + "txfs": "ff3fbf", + "txhash": "d83300bd543f7df158bc94c680ecff4a9ca863ca39016fedd9db5e121de7c562" + }, + { + "codeseparator": null, + "id": "0:193 (ff3fbf #3)", + "input": 3, + "txfs": "ff3fbf", + "txhash": "094fa663787a7bd4b1eaf206fe0f8b6a37601f570576fd487f46972d9a2d3b56" + }, + { + "codeseparator": null, + "id": "0:194 (ffff3f3f #0)", + "input": 0, + "txfs": "ffff3f3f", + "txhash": "9b198c88ff9064a9be5f9fcaeb4e28e7260af57a3a01f6b6bcaa0d774cc51620" + }, + { + "codeseparator": null, + "id": "0:195 (ffff3f3f #1)", + "input": 1, + "txfs": "ffff3f3f", + "txhash": "204bf99cc5388b206745767d8ba31a9ca69747f188f40cf450e5a4ef046b8495" + }, + { + "codeseparator": null, + "id": "0:196 (ffff3f3f #2)", + "input": 2, + "txfs": "ffff3f3f", + "txhash": "9919891586bf63fb3b8385eda9b3dd73818570c4cad9ed9501df4a65a924ff36" + }, + { + "codeseparator": null, + "id": "0:197 (ffff3f3f #3)", + "input": 3, + "txfs": "ffff3f3f", + "txhash": "f2b09f7e2532f060f4d3fafbab248b6d565669e6632b6704dbb31fea734b269c" + }, + { + "codeseparator": null, + "id": "0:198 (ffff4040 #0)", + "input": 0, + "txfs": "ffff4040", + "txhash": "daeb53bed146a3ebbb14bf2783b0c55cc049c8514f04d131bffc5fa38ba5b7a7" + }, + { + "codeseparator": null, + "id": "0:199 (ffff4040 #1)", + "input": 1, + "txfs": "ffff4040", + "txhash": "26616038778b1eb956024234df31bbc7c4206626ff5657e6e648d19a80bbe469" + }, + { + "codeseparator": null, + "id": "0:200 (ffff4040 #2)", + "input": 2, + "txfs": "ffff4040", + "txhash": "4e7cd9a09a73d8ed2abc28d095171e97c29e33fad9f75687f9b207a6a5c0f5fb" + }, + { + "codeseparator": null, + "id": "0:201 (ff008080 #0)", + "input": 0, + "txfs": "ff008080", + "txhash": "fc87d178387418de163b5cf92d7ec20236bc4e64d8013926e8202d7ffd35eaff" + }, + { + "codeseparator": null, + "id": "0:202 (ff008080 #1)", + "input": 1, + "txfs": "ff008080", + "txhash": "2f1a498e706ee6e173f8f41e79d63ab14b08b0d8107036a1a468bed5d76cfc7f" + }, + { + "codeseparator": null, + "id": "0:203 (ff008080 #2)", + "input": 2, + "txfs": "ff008080", + "txhash": "4d2a5a34873cdf4cbbc3e9629978afdce80972724a00557a0d1f4ebd3ebcaeaa" + }, + { + "codeseparator": null, + "id": "0:204 (ff008080 #3)", + "input": 3, + "txfs": "ff008080", + "txhash": "748d904461aa9fa258b2c99bfff7a46252833afce0c68786b3590506a73b4183" + }, + { + "codeseparator": null, + "id": "0:205 (ffffbfbf #0)", + "input": 0, + "txfs": "ffffbfbf", + "txhash": "eaee3dc7a879d5b4e8e8cf7d8a6a1347cb752a7f92fb1b0e2292288f874003ca" + }, + { + "codeseparator": null, + "id": "0:206 (ffffbfbf #1)", + "input": 1, + "txfs": "ffffbfbf", + "txhash": "4fe3e5742ac85e8828928f99176b02c1a2c19bc62cccf67dc1212cdd6752df7e" + }, + { + "codeseparator": null, + "id": "0:207 (ffffbfbf #2)", + "input": 2, + "txfs": "ffffbfbf", + "txhash": "78eac4bb43eff09fcb77b8a8bed50088b02692f6a9c3801f29ab7724fb408f44" + }, + { + "codeseparator": null, + "id": "0:208 (ffffbfbf #3)", + "input": 3, + "txfs": "ffffbfbf", + "txhash": "44cbb46cd43058d336d02a13d40c81cc79376b2edeede1b1e8496fb419c003a0" + }, + { + "codeseparator": null, + "id": "0:209 (ffffc0c0 #0)", + "input": 0, + "txfs": "ffffc0c0", + "txhash": "72dc250657df5833c36b1cdb6edf154199fa1736a4690f7a306be6626e73eb67" + }, + { + "codeseparator": null, + "id": "0:210 (ffffc0c0 #1)", + "input": 1, + "txfs": "ffffc0c0", + "txhash": "b7ba82f229d9597b2920f46a70769b718c96e1b616fda887b319c13b07120e23" + }, + { + "codeseparator": null, + "id": "0:211 (ffffc0c0 #2)", + "input": 2, + "txfs": "ffffc0c0", + "txhash": "7e6cb60a4e6d76f21763c15d2dcf9eafc5e28c5d7ef8e44ed614e7b848d038b9" + }, + { + "codeseparator": null, + "id": "0:212 (ffff403f #0)", + "input": 0, + "txfs": "ffff403f", + "txhash": "27bcfae81ca87df4e3db4491966ab1b46b8e4253199f30141e6003653a118f38" + }, + { + "codeseparator": null, + "id": "0:213 (ffff403f #1)", + "input": 1, + "txfs": "ffff403f", + "txhash": "fe2ac73b3a603e988033217e79355db59db6c4b2cc32eece117817091de5d9dc" + }, + { + "codeseparator": null, + "id": "0:214 (ffff403f #2)", + "input": 2, + "txfs": "ffff403f", + "txhash": "1e0a141acddecc73442d8048114b82e97f451d8de2961e6cf587622e1a2b3a85" + }, + { + "codeseparator": null, + "id": "0:215 (ffff403f #3)", + "input": 3, + "txfs": "ffff403f", + "txhash": "a0caa4c1adfd46a830a80ff16d5f0d6fae8e103818d6c5873ebd1c9d5dc6bb5e" + }, + { + "codeseparator": null, + "id": "0:216 (ffff3f40 #0)", + "input": 0, + "txfs": "ffff3f40", + "txhash": "e3be712fe64f1b6408c49a68577ecdbb85661fb1eb30f8c64ef826136a618563" + }, + { + "codeseparator": null, + "id": "0:217 (ffff3f40 #1)", + "input": 1, + "txfs": "ffff3f40", + "txhash": "82ce4badded17ab6309ddd93b920ec4f020db181afd7ef84d06165ec54b62383" + }, + { + "codeseparator": null, + "id": "0:218 (ffff3f40 #2)", + "input": 2, + "txfs": "ffff3f40", + "txhash": "36f5428b8fd687797e1a6ffe7ea068c4827442cec1ba157137ae063484011c22" + }, + { + "codeseparator": null, + "id": "0:219 (ffff0182 #0)", + "input": 0, + "txfs": "ffff0182", + "txhash": "b039ba474f3215aebdedaaaa21364dfb80f7ab575ed0c34036ca98e038cb600e" + }, + { + "codeseparator": null, + "id": "0:220 (ffff0182 #1)", + "input": 1, + "txfs": "ffff0182", + "txhash": "3238a3bfbb8540e6cce8b1e7cd154c02146ee2e251b853064ea5e593745fb6e6" + }, + { + "codeseparator": null, + "id": "0:221 (ffff0182 #2)", + "input": 2, + "txfs": "ffff0182", + "txhash": "2fc3fed971d6e1f47e2e5c64c629249e36cc453e1b2d5d63c1f99ae64da64fea" + }, + { + "codeseparator": null, + "id": "0:222 (ffff0182 #3)", + "input": 3, + "txfs": "ffff0182", + "txhash": "2bbd878f65f30f70cd8588266437db4a351009aa3b846225b78851ecb0998122" + }, + { + "codeseparator": null, + "id": "0:223 (ffffc002 #0)", + "input": 0, + "txfs": "ffffc002", + "txhash": "21e5f651af7e6632afc2024965d43418a84cadb4fa2e6f1f2e392a893208d715" + }, + { + "codeseparator": null, + "id": "0:224 (ffffc002 #1)", + "input": 1, + "txfs": "ffffc002", + "txhash": "a554a30fd4f64e3857bec009c2f8840c8f18a78e282dc13bc0c1c1dd179d6918" + }, + { + "codeseparator": null, + "id": "0:225 (ffffc002 #2)", + "input": 2, + "txfs": "ffffc002", + "txhash": "2d70285bcbf1c0e17d285ee638062ad87d27e0476c37c2a9df35f0630d776455" + }, + { + "codeseparator": null, + "id": "0:226 (ffff4101420002 #0)", + "input": 0, + "txfs": "ffff4101420002", + "txhash": "5a28f3a4cfa020612bb02906c714e1c2613e71b9cf1952370d6f1c4b207115cb" + }, + { + "codeseparator": null, + "id": "0:227 (ffff4101420002 #1)", + "input": 1, + "txfs": "ffff4101420002", + "txhash": "35c638f768e38485a73f06ba75bf776267a6a9b12fd468649ccb162e67bea679" + }, + { + "codeseparator": null, + "id": "0:228 (ffff4101420002 #2)", + "input": 2, + "txfs": "ffff4101420002", + "txhash": "7a9a70d2a89c4f3806527a1aa37193d6f6635f15ea964dde046413036da3924d" + }, + { + "codeseparator": null, + "id": "0:229 (ffff4101420002 #3)", + "input": 3, + "txfs": "ffff4101420002", + "txhash": "dc6d7fb1c630b4d61e9b8397da97956cbc61d28af56891f1160b7b8359410e78" + }, + { + "codeseparator": null, + "id": "0:230 (ffffc101c20002 #0)", + "input": 0, + "txfs": "ffffc101c20002", + "txhash": "d7b9fa4f9b9ca63d1a1706e1e584bd2aec8c347edae0228f32136a3c641d8d55" + }, + { + "codeseparator": null, + "id": "0:231 (ffffc101c20002 #1)", + "input": 1, + "txfs": "ffffc101c20002", + "txhash": "77fec0a10d21e3929b6f0c475478349e308821f4fa24185a172212a6a57d8d70" + }, + { + "codeseparator": null, + "id": "0:232 (ffffc101c20002 #2)", + "input": 2, + "txfs": "ffffc101c20002", + "txhash": "9f1a30e1cdcc5f1953174bafcb362a71fdfa43e0567153fc9d51c11785126df6" + }, + { + "codeseparator": null, + "id": "0:233 (ffffc101c20002 #3)", + "input": 3, + "txfs": "ffffc101c20002", + "txhash": "a7218901182aafd92222e698262b643f3fcb6b5cc39fe1d4b8cc3ad3859b34a2" + }, + { + "codeseparator": null, + "id": "0:234 (ffff617f627f00 #1)", + "input": 1, + "txfs": "ffff617f627f00", + "txhash": "c738cb0d15e72746087627649119db920527a33ed82b578d2d3bd207458566fc" + }, + { + "codeseparator": null, + "id": "0:235 (ffff617f627f00 #2)", + "input": 2, + "txfs": "ffff617f627f00", + "txhash": "20ea9d1416a1a5be81b0bf119fb560edadc53cb502da113e3fa25df9002f2f9e" + }, + { + "codeseparator": null, + "id": "0:236 (ffffe17fe27f00 #1)", + "input": 1, + "txfs": "ffffe17fe27f00", + "txhash": "8dac1ba3904685377c619988571028b7492785ed451c4db53fdf026ad2d6ca7d" + }, + { + "codeseparator": null, + "id": "0:237 (ffffe17fe27f00 #2)", + "input": 2, + "txfs": "ffffe17fe27f00", + "txhash": "b57661a098efc6be9ac6678673f9eb6fcdc58a4acb1961cf52db1cd2469c30e7" + }, + { + "codeseparator": null, + "id": "0:238 ( #0)", + "input": 0, + "txfs": "", + "txhash": "48fa5af5af88577b1eb3392151931216725ea6571e2eb991975b69a0ff0e4d81" + }, + { + "codeseparator": null, + "id": "0:239 ( #1)", + "input": 1, + "txfs": "", + "txhash": "cb301757616e4fac739b8a5fa3b7338690d211e0edd4b0e14627c55b3b724b67" + }, + { + "codeseparator": null, + "id": "0:240 ( #2)", + "input": 2, + "txfs": "", + "txhash": "796b94f77b4133bd78fa4dcf69fbb034ba1e6f2817eea6ea40f05190bbaed157" + }, + { + "codeseparator": null, + "id": "0:241 ( #3)", + "input": 3, + "txfs": "", + "txhash": "111ef243458f3b54fffc0d5e46d842728354aee14b1d00ef33aff0624fb6f8ec" + }, + { + "codeseparator": null, + "id": "0:242 (ff #0)", + "input": 0, + "txfs": "ff", + "txhash": "50711155a1283c2ebb56fdf37f0b12612b6c6c9d375cc7654122ba1705c384dc" + }, + { + "codeseparator": null, + "id": "0:243 (ff #1)", + "input": 1, + "txfs": "ff", + "txhash": "2cbee417d7f0c838b9725df4da4e4e29ec28e35081bccd02cb9f751012aab71b" + }, + { + "codeseparator": null, + "id": "0:244 (ff #2)", + "input": 2, + "txfs": "ff", + "txhash": "2b860da4aeed7343c163dd9e54f1de682faa38bb9ba363b1570a5454bd280e37" + }, + { + "codeseparator": null, + "id": "0:245 (ff #3)", + "input": 3, + "txfs": "ff", + "txhash": "8b026e0a530a46af2c5868878847234bf2c33d5587c4b2bfba6f334101a4068f" + }, + { + "codeseparator": null, + "id": "0:246 (f7 #0)", + "input": 0, + "txfs": "f7", + "txhash": "d544b6d414ed5cfd25b2c52773e42ba8dfabc20a3c578d0bdca72821fb27aea9" + }, + { + "codeseparator": null, + "id": "0:247 (f7 #1)", + "input": 1, + "txfs": "f7", + "txhash": "4ba682178494a57b51180b7c7db4d809c0b3a9b22cf59dd14be6a44027f52d94" + }, + { + "codeseparator": null, + "id": "0:248 (f7 #2)", + "input": 2, + "txfs": "f7", + "txhash": "0e0124b6ef1d3f3c4c91b40c76fe4e7466a2a9192ce51e8b1de063f06208053e" + }, + { + "codeseparator": null, + "id": "0:249 (f3 #0)", + "input": 0, + "txfs": "f3", + "txhash": "e01a7f5d380ef53a929d9e6167354e96af923c00e787ad88ce263a899f713e6f" + }, + { + "codeseparator": null, + "id": "0:250 (f3 #1)", + "input": 1, + "txfs": "f3", + "txhash": "2bce00f52593a09892f775bfbfd7ba7af786fce22eae0e45ae5fb70b75e09807" + }, + { + "codeseparator": null, + "id": "0:251 (f3 #2)", + "input": 2, + "txfs": "f3", + "txhash": "8169a74969f9ddf2de7f1773fc39692eb25f9f93e496120cdc610207090d20fe" + }, + { + "codeseparator": null, + "id": "0:252 (f3 #3)", + "input": 3, + "txfs": "f3", + "txhash": "de917cd926d5df98bfdf8b0a81e6b0d2c087ad25fad5603461f403aadd43a160" + }, + { + "codeseparator": null, + "id": "0:253 (fd #0)", + "input": 0, + "txfs": "fd", + "txhash": "c59f4e1ad43ca2f2ecd2a16591600a60bf85b137789816b0cccc88b968805584" + }, + { + "codeseparator": null, + "id": "0:254 (fd #1)", + "input": 1, + "txfs": "fd", + "txhash": "aa46bbae338a8a1f4c542848f0ee56ac10257c7377fe9b78436b8c21005ff1a7" + }, + { + "codeseparator": null, + "id": "0:255 (fd #2)", + "input": 2, + "txfs": "fd", + "txhash": "6b0b40b4cc40a74101767f383c8b0ad3876c8abb5cea65a4afe741721abdf30e" + }, + { + "codeseparator": null, + "id": "0:256 (fd #3)", + "input": 3, + "txfs": "fd", + "txhash": "e8680f7a4ce02ca8b2205f965b7fca5e747f2df897d412f265857df3a75e921b" + }, + { + "codeseparator": null, + "id": "0:257 (f5 #0)", + "input": 0, + "txfs": "f5", + "txhash": "09078b784d01f07e8dc2f9f82d08f37fb132647de4b9c1fce1ed7908d5ad4244" + }, + { + "codeseparator": null, + "id": "0:258 (f5 #1)", + "input": 1, + "txfs": "f5", + "txhash": "4373b866a5cd7712cf70153dc755b94e0606d8ff64f5f2e8e0320ddebd1e98c0" + }, + { + "codeseparator": null, + "id": "0:259 (f5 #2)", + "input": 2, + "txfs": "f5", + "txhash": "a16d08e196fd244d0702f65d401ebb198f146cd466eb8b3ed267c4f20f0c58a3" + }, + { + "codeseparator": null, + "id": "0:260 (f1 #0)", + "input": 0, + "txfs": "f1", + "txhash": "6a78d6b80384fe73dd8b86f68e058e6f7a014d4dd33ecbe3239c372ea3fdf76f" + }, + { + "codeseparator": null, + "id": "0:261 (f1 #1)", + "input": 1, + "txfs": "f1", + "txhash": "a2eda8055dee49f5fe9c51862ba9cd0afa2d6efeb4164c0c0b84508253b41f30" + }, + { + "codeseparator": null, + "id": "0:262 (f1 #2)", + "input": 2, + "txfs": "f1", + "txhash": "b92072d9fa749e9d030f1aa95fbf67d784d1103403522ff0ac989f2538a265db" + }, + { + "codeseparator": null, + "id": "0:263 (f1 #3)", + "input": 3, + "txfs": "f1", + "txhash": "ee2d20a1ea7967dda278fe9f0d8aac7ed21bf9eb97786952b998f34e769f51d3" + }, + { + "codeseparator": null, + "id": "0:264 (ed #0)", + "input": 0, + "txfs": "ed", + "txhash": "0b4e8b72607a96a020f9915c8e3cb1ee56eea93c299de1cdc126c63cf458dd75" + }, + { + "codeseparator": null, + "id": "0:265 (ed #1)", + "input": 1, + "txfs": "ed", + "txhash": "98bfc246c2ba4594738ca07d0bb0ac299269af0bedf67d511646d2451a05df52" + }, + { + "codeseparator": null, + "id": "0:266 (ed #2)", + "input": 2, + "txfs": "ed", + "txhash": "e5a6ce9c6838f4fa78192d29b8e2b1624a66e969744b5fdf34768c64da5b6408" + }, + { + "codeseparator": null, + "id": "0:267 (ed #3)", + "input": 3, + "txfs": "ed", + "txhash": "86825b690fca2ca2f22f8ba883f951bf9fc492661bd4057e4bff518709d912d2" + }, + { + "codeseparator": null, + "id": "0:268 (e5 #0)", + "input": 0, + "txfs": "e5", + "txhash": "ac4b1f8a07dd18a210fc4a22309ca406d1c8e5576336a368f56bd119832299cf" + }, + { + "codeseparator": null, + "id": "0:269 (e5 #1)", + "input": 1, + "txfs": "e5", + "txhash": "0c815e13f6dab7f74c052d9cce1d75c74238d831cc34b42cd5f8ecac7c66c769" + }, + { + "codeseparator": null, + "id": "0:270 (e5 #2)", + "input": 2, + "txfs": "e5", + "txhash": "5ed27a6f659f521391921fb0b247314d20175fb181498d0e32a8ed4265e858f4" + }, + { + "codeseparator": null, + "id": "0:271 (e1 #0)", + "input": 0, + "txfs": "e1", + "txhash": "b97f04f9ca3f5df1012d941aa21961a55e262c3e48d69b8b00bfb438896ea88e" + }, + { + "codeseparator": null, + "id": "0:272 (e1 #1)", + "input": 1, + "txfs": "e1", + "txhash": "7ff8bff632fe92a2b58ecb5288093889cba134d38ba2b5483385277443cdcd0a" + }, + { + "codeseparator": null, + "id": "0:273 (e1 #2)", + "input": 2, + "txfs": "e1", + "txhash": "3c601429053cdda95fb3bf39013452afec09c83b0992303a0a1d6235a3c327ab" + }, + { + "codeseparator": null, + "id": "0:274 (e1 #3)", + "input": 3, + "txfs": "e1", + "txhash": "7bd7bc63f46839ca9941cdc66037c75ae63891e4b85d384ed9bb825113002759" + }, + { + "codeseparator": null, + "id": "0:275 (cd #0)", + "input": 0, + "txfs": "cd", + "txhash": "66617fc93f30d3fcf0187eb8c91fa4720c38a8fe8f0092c4dc1fcacd15dd901a" + }, + { + "codeseparator": null, + "id": "0:276 (cd #1)", + "input": 1, + "txfs": "cd", + "txhash": "727a5e73d8d0c0888a34191de9a42ea88f9683d5e038056b0ed54b9fbefe09c1" + }, + { + "codeseparator": null, + "id": "0:277 (cd #2)", + "input": 2, + "txfs": "cd", + "txhash": "1873cb32776847e48410c4a3dde7031064be6357ee131e0114e30c02ac5aa14c" + }, + { + "codeseparator": null, + "id": "0:278 (cd #3)", + "input": 3, + "txfs": "cd", + "txhash": "93a6160f4993c6580a66300c84451e8983f8625f1fcb998453c8d4f08ec0c22a" + }, + { + "codeseparator": null, + "id": "0:279 (c5 #0)", + "input": 0, + "txfs": "c5", + "txhash": "87c88362178ffbdee97b81b32c1c769d2db4deadc8d01bef98e40022afb413ba" + }, + { + "codeseparator": null, + "id": "0:280 (c5 #1)", + "input": 1, + "txfs": "c5", + "txhash": "4470477d28d1c081433261c23439d77b4d2ff6ccf598f3a2f1283fa3fc371856" + }, + { + "codeseparator": null, + "id": "0:281 (c5 #2)", + "input": 2, + "txfs": "c5", + "txhash": "dec04225d95a1f728b4fc14911aa5822ffa08bcb5384c40cb9b264b85dce7ebe" + }, + { + "codeseparator": null, + "id": "0:282 (c1 #0)", + "input": 0, + "txfs": "c1", + "txhash": "3531e1f3e5d5a4c609282aef0e1781b48301e1aae1fd9d940afdc6f82cce2c56" + }, + { + "codeseparator": null, + "id": "0:283 (c1 #1)", + "input": 1, + "txfs": "c1", + "txhash": "a5643b5c500b55287a7753d2569b7f9072a399751bbb7ad52f484ae5d85fb3a6" + }, + { + "codeseparator": null, + "id": "0:284 (c1 #2)", + "input": 2, + "txfs": "c1", + "txhash": "5679c7ae2e14ce2dc426112640f9591eec1cb98105a65217ce0bead76253ca59" + }, + { + "codeseparator": null, + "id": "0:285 (c1 #3)", + "input": 3, + "txfs": "c1", + "txhash": "d6e64a7c01f28a5600d555e7aa7de2defdbcc7682de10fc1c21b17f1c3f7765f" + } + ] + } +] \ No newline at end of file