From 54150c733ea0c2e0df67ba469770016f35e47930 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Wed, 6 Sep 2023 16:16:35 +0100 Subject: [PATCH 1/4] Add BIP for OP_TXHASH and OP_CHECKTXHASHVERIFY --- bip-txhash.md | 273 ++++ bip-txhash/ref-impl/Cargo.toml | 14 + bip-txhash/ref-impl/src/main.rs | 595 +++++++++ bip-txhash/ref-impl/txhash_vectors.json | 1591 +++++++++++++++++++++++ 4 files changed, 2473 insertions(+) create mode 100644 bip-txhash.md create mode 100644 bip-txhash/ref-impl/Cargo.toml create mode 100644 bip-txhash/ref-impl/src/main.rs create mode 100644 bip-txhash/ref-impl/txhash_vectors.json diff --git a/bip-txhash.md b/bip-txhash.md new file mode 100644 index 0000000000..7deef3a603 --- /dev/null +++ b/bip-txhash.md @@ -0,0 +1,273 @@ +``` + BIP: tbd + Layer: Consensus (soft fork) + Title: OP_TXHASH and OP_CHECKTXHASHVERIFY + Author: Steven Roose + Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-tbd + 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. + + +# Summary + +## OP_CHECKTXHASHVERIFY + +The first new opcode, `OP_CHECKTXHASHVERIFY`, redefines the `OP_NOP4` opcode (`0xb3`) as a soft fork upgrade. + +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, fail otherwise. +* The first 32 bytes are interpreted as the TxHash and the remaining suffix bytes specify the TxFieldSelector. +* If the TxFieldSelector is invalid, fail. +* The actual TxHash of the transaction at the current input index, calculated + using the given TxFieldSelector must be equal to the first 32 bytes of the + element on the stack, fail otherwise. + + +## OP_TXHASH + +The second new opcode, `OP_TXHASH`, redefines the `OP_SUCCESS189` tapscript opcode (`0xbd`) as a soft fork upgrade. + +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. +* If the TxFieldSelector is invalid, fail. +* 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. + + Special case `TXFS_SPECIAL_TEMPLATE` is 4 bytes long, as follows: + *  1. `TXFS_ALL` + *  2. `TXFS_INPUTS_TEMPLATE | TXFS_OUTPUTS_ALL` + *  3. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + *  4. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + + * the `0x00` byte: it is set equal to `TXFS_SPECIAL_ALL`, which means "ALL" and is primarily + useful to emulate `SIGHASH_ALL` when `OP_TXHASH` is used in combination + with `OP_CHECKSIGFROMSTACK`. + + Special case `TXFS_SPECIAL_ALL` is 4 bytes long, as follows: + *  1. `TXFS_ALL` + *  2. `TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL` + *  3. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + *  4. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + +* 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. (unused) + +* The last (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.) + +* If either "inputs" or "outputs" is set to 1, expect another byte with its 8 + bits assigning 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_VALUED`) + *  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_INPUTS | TXFS_OUTPUTS | 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_INPUTS_TEMPLATE = TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_INPUTS_PREV_VALUES | TXFS_INPUTS_TAPROOT_ANNEXES` + * `TXFS_OUTPUTS_ALL = TXFS_OUTPUTS_SCRIPTPUBKEYS | TXFS_OUTPUTS_VALUES` + +For both inputs and then outputs, do the following: + +* If the "in/outputs" field is set to 1, another additional byte is expected: + * 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_SELECTION_SIZE`) is + used to indicate the "count 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 remaining lowest 6 bits of the first byte will be + interpreted as `n`, the number of individual in/outputs to select. For each + individual input, (at least) one byte is expected, of this byte. The + highest bit is used to indicate "absolute or relative" indices. + * If the highest bit is set to 0, it is an absolute index. The second + highest bit is used to indicate the amount of bytes are used to represent + the index. + * If the second-highest bit is 0, the remaining 6 bits represent the index to be selected. + * If the second-highest bit is 1, the remaining 6 bits, together with the 8 bits of the next + byte, represent the index to be selected. + * If the highest bit is set to 1, it is a relative index. The second highest bit is used to + indicate the sign of the index. + * If the second-highest bit is set to 0, the remaining 6 bits represent the positive relative + index to be selected. + * If the second-highest bit is set to 1, the remaining 6 bits represent the negative relative + index to be selected. + +Effectively, this allows a user to select +* all in/outputs +* the current input index +* the leading in/outputs up to 8192 +* up to 64 individually selected in/outputs +** using absolute indices up to 16384 +** using indices relative to the current input index from -64 to +64. + +The TxFieldSelector is invalid when +* a byte is expected but missing +* additional unexpected bytes are present +* index size is set to 1 while not being necessary +* a leading number of individual index is selected out of bounds of the in/outputs +* individual indices are duplicated or not in increasing order + +These limitations are to avoid potential TxFieldSelector malleability. It is +however allowed to use leading mode where it could be "all". This +is important to allow for optional addition of extra inputs or outputs. + +//TODO(stevenroose) should we disallow individual that could be leading? + + +## 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 10. 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 effectively the same (though not + byte-for-byte identical) as what `OP_CTV` acomplishes, 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. + + +# 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. + + +# 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-txhash/ref-impl/Cargo.toml b/bip-txhash/ref-impl/Cargo.toml new file mode 100644 index 0000000000..e17f8d49c7 --- /dev/null +++ b/bip-txhash/ref-impl/Cargo.toml @@ -0,0 +1,14 @@ +[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" } +bitcoin-io = { git = "https://github.com/stevenroose/rust-bitcoin.git", branch = "txhash" } diff --git a/bip-txhash/ref-impl/src/main.rs b/bip-txhash/ref-impl/src/main.rs new file mode 100644 index 0000000000..a41b0ecb96 --- /dev/null +++ b/bip-txhash/ref-impl/src/main.rs @@ -0,0 +1,595 @@ + +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_CONTROL_BLOCK: u8 = 1 << 3; +pub const TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS: u8 = 1 << 4; +pub const TXFS_INPUTS: u8 = 1 << 5; +pub const TXFS_OUTPUTS: u8 = 1 << 6; + +pub const TXFS_CONTROL: u8 = 1 << 7; + +pub const TXFS_ALL: u8 = TXFS_VERSION + | TXFS_LOCKTIME + | TXFS_CURRENT_INPUT_IDX + | TXFS_CURRENT_INPUT_CONTROL_BLOCK + | TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS + | TXFS_INPUTS + | TXFS_OUTPUTS + | TXFS_CONTROL; + +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_INPUTS_TEMPLATE: u8 = TXFS_INPUTS_SEQUENCES + | TXFS_INPUTS_SCRIPTSIGS + | 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_SELECTION_SIZE: u8 = 1 << 5; +pub const TXFS_INOUT_SELECTION_MASK: u8 = + 0xff ^ TXFS_INOUT_NUMBER ^ TXFS_INOUT_SELECTION_MODE ^ TXFS_INOUT_SELECTION_SIZE; + + +pub const TXFS_SPECIAL_ALL: [u8; 4] = [ + TXFS_ALL, + TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL, + TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL, + TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL, +]; +pub const TXFS_SPECIAL_TEMPLATE: [u8; 4] = [ + TXFS_ALL, + TXFS_INPUTS_TEMPLATE | 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(&[]); + +/// 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, +) -> Result<(Vec, bool), &'static str> { + let first = bytes.next().ok_or("in/output bit set but selection byte missing")?; + let commit_number = (first & TXFS_INOUT_NUMBER) != 0; + let selection = first & (0xff ^ TXFS_INOUT_NUMBER); + + let selected = if selection == TXFS_INOUT_SELECTION_NONE { + if !commit_number { + return Err("no in/output selection given and nb_items bitflag also unset"); + } + 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_SELECTION_SIZE) == 0 { + (selection & TXFS_INOUT_SELECTION_MASK) as usize + } else { + if (selection & TXFS_INOUT_SELECTION_MASK) == 0 { + return Err("non-minimal leading selection"); + } + 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 count = (selection & TXFS_INOUT_SELECTION_MASK) as usize; + if count == 0 { + return Err("can't select 0 in/outputs in individual mode"); + } + + let mut selected = Vec::with_capacity(count as usize); + for _ in 0..count { + let idx = if (selection & TXFS_INOUT_SELECTION_SIZE) == 0 { + bytes.next().ok_or("not enough single-byte indices")? as usize + } else { + let first = bytes.next().ok_or("first byte of two-byte index missing")?; + let second = bytes.next().ok_or("second byte of two-byte index missing")?; + (first as usize) << 8 + (second 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)) +} + +/// +/// +/// 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 + } else if txfs.len() == 1 && txfs[0] == 0x00 { + &TXFS_SPECIAL_ALL + } else { + txfs + }; + + let mut engine = sha256::Hash::engine(); + + if (txfs[0] & TXFS_CONTROL) != 0 { + engine.input(txfs); + } + + let mut bytes = txfs.iter().copied(); + 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_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(); + } + + // Stop early if no inputs or outputs are selected. + if (global & TXFS_INPUTS) == 0 && (global & TXFS_OUTPUTS) == 0 { + if txfs.len() > 1 { + return Err("input and output bit unset and more than one byte in txfs"); + } + return Ok(sha256::Hash::from_engine(engine)); + } + + // Now that we know we have some inputs and/or some outputs to commit. + let inout_fields = bytes.next().ok_or("in- or output bit set but only one byte")?; + + if (global & TXFS_INPUTS) == 0 { + if (inout_fields & TXFS_INPUTS_ALL) != 0 { + return Err("inputs bit not set but some input field bits set"); + } + } else { + let (selection, commit_number) = parse_inout_selection( + &mut bytes, tx.input.len(), current_input_idx, + )?; + + if (inout_fields & TXFS_INPUTS_ALL) == 0 && !selection.is_empty() { + return Err("input selection given but no input field bits set"); + } + + if commit_number { + (tx.input.len() as u32).consensus_encode(&mut engine).unwrap(); + } + + if !selection.is_empty() && (inout_fields & TXFS_INPUTS_PREVOUTS) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &selection { + tx.input[*i].previous_output.consensus_encode(&mut engine).unwrap(); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !selection.is_empty() && (inout_fields & TXFS_INPUTS_SEQUENCES) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &selection { + tx.input[*i].sequence.consensus_encode(&mut engine).unwrap(); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !selection.is_empty() && (inout_fields & TXFS_INPUTS_SCRIPTSIGS) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &selection { + engine.input(&sha256::Hash::hash(&tx.input[*i].script_sig.as_bytes())[..]); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !selection.is_empty() && (inout_fields & TXFS_INPUTS_PREV_SCRIPTPUBKEYS) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &selection { + engine.input(&sha256::Hash::hash(&prevouts[*i].script_pubkey.as_bytes())[..]); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !selection.is_empty() && (inout_fields & TXFS_INPUTS_PREV_VALUES) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &selection { + prevouts[*i].value.consensus_encode(&mut engine).unwrap(); + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); + } + + if !selection.is_empty() && (inout_fields & TXFS_INPUTS_TAPROOT_ANNEXES) != 0 { + let hash = { + let mut engine = sha256::Hash::engine(); + for i in &selection { + 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[..]); + } + } + + if (global & TXFS_OUTPUTS) == 0 { + if (inout_fields & TXFS_OUTPUTS_ALL) != 0 { + return Err("outputs bit not set but some output field bits set"); + } + } else { + let (selection, commit_number) = parse_inout_selection( + &mut bytes, tx.output.len(), current_input_idx, + )?; + + if (inout_fields & TXFS_OUTPUTS_ALL) == 0 && !selection.is_empty() { + return Err("output selection given but no output field bits set"); + } + + 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(); + } + } + + Ok(sha256::Hash::from_engine(engine)) +} + +mod test_vectors { + use super::*; + 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 selectors: &[&[u8]] = &[ + // global + &[1 << 0], + &[1 << 1], + &[1 << 2], + &[1 << 3], + &[1 << 4], + &[0x9f], + // outputs + &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_SELECTION_ALL], + &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_SELECTION_ALL], + &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_SELECTION_ALL], + &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + // inputs + &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + // both + &[0xff, 0xff, TXFS_INOUT_SELECTION_ALL, TXFS_INOUT_SELECTION_ALL], + &[0xff, 0xff, TXFS_INOUT_SELECTION_CURRENT, TXFS_INOUT_SELECTION_CURRENT], + &[0xff, 0xff, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE, + TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], + &[0xff, 0xff, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL, + TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + &[0xff, 0xff, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT, + TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], + &[0xff, 0xff, TXFS_INOUT_SELECTION_CURRENT, TXFS_INOUT_SELECTION_ALL], + &[0xff, 0xff, TXFS_INOUT_SELECTION_ALL, TXFS_INOUT_SELECTION_CURRENT], + // leading + &[0xff, 0xff, 0x01, 0x02], + // individual + &[0xff, 0xff, TXFS_INOUT_SELECTION_MODE | 0x01, 0x01, + TXFS_INOUT_SELECTION_MODE | 0x02, 0x00, 0x02], + &[0xff, 0xff, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_MODE | 0x01, 0x01, + TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_MODE | 0x02, 0x00, 0x02], + //TODO(stevenroose) test index size, but for that we need > 32 in/outputs + // special cases + &[], + &[0x00], + ]; + + let cases = vec![ + test_vector_tx(), + ]; + + let out_selector = |txfs: &[u8]| { + if txfs == &[0x00] || txfs.get(0)? & TXFS_OUTPUTS == 0 { + None + } else if txfs.get(0)? & TXFS_INPUTS == 0 { + Some(txfs[2]) + } else { + Some(txfs[3]) + } + }; + + cases.into_iter().map(|(tx, prevs)| { + let mut vectors = Vec::new(); + for txfs in selectors { + for i in 0..tx.input.len() { + if i >= tx.output.len() { + if let Some(outs) = out_selector(txfs) { + if (outs & (0xff ^ TXFS_INOUT_NUMBER)) == TXFS_INOUT_SELECTION_CURRENT { + continue; + } + } + } + + vectors.push(TestVector { + txfs: txfs.to_vec(), + input: i, + codeseparator: None, + txhash: calculate_txhash(txfs, &tx, &prevs, i as u32, None).unwrap(), + }); + } + } + 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().map(|c| serde_json::json!({ + "tx": serialize_hex(&c.tx), + "prevs": c.prevs.iter().map(|p| serialize_hex(p)).collect::>(), + "vectors": c.vectors.into_iter().map(|v| serde_json::json!({ + "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-txhash/ref-impl/txhash_vectors.json b/bip-txhash/ref-impl/txhash_vectors.json new file mode 100644 index 0000000000..b8f91972e0 --- /dev/null +++ b/bip-txhash/ref-impl/txhash_vectors.json @@ -0,0 +1,1591 @@ +[ + { + "prevs": [ + "68010000000000000160", + "69010000000000002200200100000000000000000000000000000000000000000000000000000000000000", + "69010000000000002251200100000000000000000000000000000000000000000000000000000000000000", + "6a010000000000002251200200000000000000000000000000000000000000000000000000000000000000" + ], + "tx": "02000000000104111111111111111111111111111111111111111111111111111111111111111101000000012301000000222222222222222222222222222222222222222222222222222222222222222202000000000300000033333333333333333333333333333333333333333333333333333333333333330300000000020000004444444444444444444444444444444444444444444444444444444444444444040000000003000000035e0100000000000001565f0100000000000001576101000000000000015800030113011402504201011203011301140250422a000000", + "vectors": [ + { + "codeseparator": null, + "input": 0, + "txfs": "01", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "01", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "01", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "01", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "02", + "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "02", + "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "02", + "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "02", + "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "04", + "txhash": "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "04", + "txhash": "67abdd721024f0ff4e0b3f4c2fc13bc5bad42d0b7851d456d88d203d15aaa450" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "04", + "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "04", + "txhash": "9d9f290527a6be626a8f5985b26e19b237b44872b03631811df4416fc1713178" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "08", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "08", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "08", + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "08", + "txhash": "d703d3da6a87bd8e0b453f3b6c41edcc9bf331b2b88ef26eb39dc7abee4e00a3" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "10", + "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "10", + "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "10", + "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "10", + "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "9f", + "txhash": "4f248b4664d9b7eb78506ee3ae3d40f45b65b2c34f5472bb5e9c367770e7f1ee" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "9f", + "txhash": "634565d0889156efa4c3a932c6b832aaeac34af7e77245b1c1db8ed260ad4e1d" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "9f", + "txhash": "55d54935fbc0dbc572da8ad1eb47c9e3fc28449a070d59a4a2c180f6dc33b285" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "9f", + "txhash": "51f86e7bb438301882176aa6dff4ba755fed8cf4d2948bf54bcd143bce135376" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df4040", + "txhash": "295da1be3ab5d30bbdbc803ae25066922d0ad964dc08654fc14966d4636271c3" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df4040", + "txhash": "76c6722f456306260a5c91037dfec2d5bd586ab1f21039216f74455150b221d4" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df4040", + "txhash": "78cc0e2841fc0c261d98a577ec46af84c0a19ff2d39a9a9e31dec0a5bfab0f09" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df8040", + "txhash": "563f4ebd7bb6f7f4313eaadab32a1e8b9f949227d0cde6b15712b0f26f25dd03" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df8040", + "txhash": "1b0b39caf5579978f8ad3542e58a5a5a02e328be0ab357153a7673108a7edf21" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df8040", + "txhash": "a145587dce2a85b50bdd085568062612ca3b1ebd4a4ad61f0c765799fd804195" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "dfc040", + "txhash": "7a4f203d00749f677f7cb83043ac4eeb803099063cd01127aeb984bdc4632d7d" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "dfc040", + "txhash": "200caf79e16000d8d83cb0d954904ae0780bcae952ac7fa283517e6b8eae69f2" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "dfc040", + "txhash": "2cc49b1f27c090bbc7bf1e4e4916b3225a75fdae44be3dadb37b0ee32b1f4499" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df403f", + "txhash": "5a141494a4ac6a3206fbf0290c094b1aff30638f762f8d15ebea9a1eb98ee309" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df403f", + "txhash": "0aaee2fb5fcb20b261346efadbb2165823e24d1e4613ee14396815381172db64" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df403f", + "txhash": "c51cf8d872616d08f467eade57662b3944b872929c454e71936d315b76683cc6" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "df403f", + "txhash": "36a1135d6bd5585ce45c07813aca090f26acd04501789c0a3c59d5de613f5082" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df803f", + "txhash": "4c4364e3e8642aede7454bc332ce5c9973ef6ad6a9ec8817f706f73c9fbfdc24" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df803f", + "txhash": "d619772a10b0fd8f390744a47aba9761deb00798235499253c67e54ce0408b0b" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df803f", + "txhash": "4f2f21a2e853e09daff7540d095f214360ba1172a31ed137ad248229ef9f2df8" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "df803f", + "txhash": "03eb47fa98296ec7ac24c0e91c7fc8f79080af737c7b0c9508aae2a58cb9968e" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "dfc03f", + "txhash": "49ec992a00d9ddfba9bfe933cf0325a1aa0f97e58382924c6291aca0cb0cc36b" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "dfc03f", + "txhash": "31cb19d1a5d718ad03459183a66e5ea630603ee9efea2219b11e95861561dbb6" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "dfc03f", + "txhash": "33862645c5a38f881c35a1b86b56532600be24e3fa6568ec8b97f756192f4a56" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "dfc03f", + "txhash": "8aaa0dd9ed60315a5b55d20e4ca7aa180c5c5f7becaddf93fc43211aa69cdf9e" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df4080", + "txhash": "df534a79f3487d041c71ac83e98bd4a8d62b46311ad8521aeab5dce996212697" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df4080", + "txhash": "160c9bb49915d462f30cd8d73f1ad91790f426781a671add821838a6e29df7df" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df4080", + "txhash": "5883c4220597e5651c139913c4f4f172ddf03ecb3a4324323e325bb4df7a830d" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "df4080", + "txhash": "0623a9a97b6265f00e498e0e4f9173f6436221f5b7321877f3bf89f7deb1c65a" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df8080", + "txhash": "b3c900a1547c86bbec94d19ed18b6324f79c6945d200fa333ade3ad934efd606" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df8080", + "txhash": "7f9ec8329c6ed349f7f39ce1a3527aaa9cb0a1cfe95201a267db74416dbca620" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df8080", + "txhash": "abe5b10c15a23ddb09d86fe13108564f08c7cdaf4044eea17697b6c50889af79" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "df8080", + "txhash": "55102c3467fc5b299def1e1103ae4a59a784200904e755a284335a92d31513df" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "dfc080", + "txhash": "086c9be876b432efcc4a82d8b5aa1eea25cdde324552a0c6acce090b45a7f2eb" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "dfc080", + "txhash": "9e1c87642dfe7451423094f60009c689a88efd9c9a2453f8355e0df9d3e2c821" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "dfc080", + "txhash": "d14524a4b2de734214a14eed77505dea60ddf90e92d2dee2b4c561f83de70d6c" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "dfc080", + "txhash": "ddc9da80b68d18e7f810668a9e185a4066c0a96c016c510947aabaf2348b5305" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df40c0", + "txhash": "7cabeeea3d2abdfec93ee33c1e7197b8255b342bdfeff200b01f1fa4fdadbe91" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df40c0", + "txhash": "83c9657bb3ccf6b5bfd1ee3a38c2fafdeb1d843526cad53aef57a9b6cd13965d" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df40c0", + "txhash": "1343074535405fd18da5e7a78a9ee76dcb0a20c53eb4894c8f577499a2b4ead5" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df80c0", + "txhash": "3a983b896e2a5a0686fe65cb481d2cb367c80cdb1ab3e3be8baec53237eb88eb" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df80c0", + "txhash": "f7a552eb23e2a4f36fe4b72371e1e2808637f6bc4ea66af789646f404f61ba24" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df80c0", + "txhash": "c118fc8b23e2414bfaab1503515529dd5f916c5202dc662e02dcd0f0b6fdaf8a" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "dfc0c0", + "txhash": "2bd80d3ac416e2bcc2db3aacdf42e8bfa37f0e66d4151e73ce1030560b457eb7" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "dfc0c0", + "txhash": "23f3048aaa59b549b4fe9160f0a0ba3d015cb453256b09b588715913dce24d19" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "dfc0c0", + "txhash": "dd964c815a875dc3abef1c32f790597d23e82cf1c583ab2a1d48f0c0143aab78" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df40bf", + "txhash": "da59a82b3d3485c4830add922bf465a942c3c9a16c6d3ddde84e79e0975d15ed" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df40bf", + "txhash": "b92124b11d31d9d8e2fe0e0f82ef582b1cd7a59c16ae61a641b8a2ee947314d5" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df40bf", + "txhash": "79b25aea531bd1138fa6afa1d1f6d4eace1924be8a7a9222503c7613ce5ba0e9" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "df40bf", + "txhash": "22eeb3eb26b54f20aa20b94eb9c5eaf47e3989b2681327f81fee9f6a12479dfc" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "df80bf", + "txhash": "acde5987109fbd603ba4fe7f3e539493409daa5c8a80f5b14e071f2729afcdcc" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "df80bf", + "txhash": "95aa52f89dd3672e2db811a5da70b23d0620e2d7d78ac55746438db53420628e" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "df80bf", + "txhash": "34704e9968c0670f47e97b1fa77e6e441d2cbd97f23aae216654820a97a3119f" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "df80bf", + "txhash": "904338c16b423808e8d3bf491c9ab46308cff6ec09025b20ee288681206133ff" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "dfc0bf", + "txhash": "d46d78ffaaa4264075c102a2792f35cbd7cb0d2bba46f84a74d3bcc0e2fd4a07" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "dfc0bf", + "txhash": "209191f354e31d55cef29adee64c5c70502a6a13d6bf3e93e2cce3cd13d86bde" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "dfc0bf", + "txhash": "a50ecaa5a999547054a44367cdcceb537c75a4b16a2bf43da3f08ef1909725ff" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "dfc0bf", + "txhash": "0c3e7985a168c152f450d39279f9d0a8d8ec0142af05018b8b88e04d3516e73e" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf0140", + "txhash": "91d38e0ca0e5095249ddd5c6ea15c98eb09ad84935ee15b078ce16e80062c87b" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf0140", + "txhash": "99e5d133679c749fe3f7a990205aa8b86808bde5d98a563e049eb887def6961d" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf0140", + "txhash": "fe8a42c47ff81de1664346ba78ac501a7aaf57d56ab5d7edac26220e07420efc" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf0140", + "txhash": "e5ec50028b9a3953769c22d5042baf2c0264710467b802d85c8a0967aa80d690" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf0240", + "txhash": "ffb8f7ca010f0a1bed083290a7428115ab9a526a6986d169045f987826c3e5a2" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf0240", + "txhash": "56092aa1f0a8b76bd1aa16e8db211d9627f007b9f58a605c16cbd39d3b7d2542" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf0240", + "txhash": "060bce50807d4bb8662bc9535372d33f649bda3512f4c351b60304e1e3d82ad7" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf0240", + "txhash": "4223ea00d094655082bf73cd3fcab91de3b8f8939c56228deee6be429c6c54a5" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf0440", + "txhash": "9ee281b8f2085859d08cf70696fe023761ac32191767f09ce34179e0a5e21366" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf0440", + "txhash": "c0dc425b65f85631add220410540c860733ec1d0d0e8099e8fdcf8d3b437fd39" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf0440", + "txhash": "d6d195953a0bd49ccca47f604f04135330420528bf9a542fab16fa2e7b582568" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf0440", + "txhash": "a34f814e1c1ac0a9f429cd48675c26d89bc23d3e358a3343734423445a76a890" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf0840", + "txhash": "112c0da280dd4c137c7aee304d3171b0a28b9fa218cc869570b2916a579a15a3" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf0840", + "txhash": "54244057189beb0ee25325b4f2d2814a471b04de0a5a0906c5b0b56d97444f43" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf0840", + "txhash": "0c136aab4e6a317299df526a51714951741b32ff8d4caf5cb3d16984b5c9d7e0" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf0840", + "txhash": "4e520f2941db8d101b9d96a7899796954df87f0ec09944a77c1b4bfb332809c2" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf1040", + "txhash": "dbd2f02312c7442ab580a8bb45711688f5c1142555fd8c94dac76f72925b1aff" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf1040", + "txhash": "5073580ef20d94a230bfdfe3f4d850a0d4a405957e46262ec2c02d3c53ce6d00" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf1040", + "txhash": "e2aae3653e626a6f56e5901d44de6c8a2f65fcdaaa21552ad4a9b3263cfbf393" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf1040", + "txhash": "cfdc04bd6c444eb9bb0e2db32db7bb66542c7ae5f99d831ed899d50a7f3ccb11" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf2040", + "txhash": "5144500234219dac2bfd9f1bb8a92886e5f8b143e009341936a12a6fb26c750a" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf2040", + "txhash": "1adbc6c62633fd18c1d0570d3c8a8f9bb0ea073aa8a79995a9cfed622a4608a4" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf2040", + "txhash": "7c2c214489c1496236c71cf5a47c77979b81604dd0b1a6aa4eabcb867dfd3bae" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf2040", + "txhash": "9095453a420b8f59a62368eee24769741241c15aa99e74cbf8ce029b28a5b8bc" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf3f40", + "txhash": "4b1e78f5da41122f5155ba68146cf5100baa90a3e92e44009eb18ec8c2c11e55" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf3f40", + "txhash": "58db27da765fca79e76f2d5b45b8a215e8e2dfd57f3e624d5ba700150ff72a10" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf3f40", + "txhash": "32f66cc966a04e69dd72b9ffb1dbf58edc7b1d51fde4084cee0dc8000374fda5" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf3f40", + "txhash": "1dd603250c852348e98057583c81fb31bcdf4918a7023256b047f5acd46d81ee" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf013f", + "txhash": "c133c0a4d74c5736ef471afc33d8cc7aa91ecfea07509458f127e6fde2400676" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf013f", + "txhash": "704cdcaffd4c954061ec58b872fbee9c2b94b7385de9e11b5b5d1e43e888e510" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf013f", + "txhash": "cc02ac60a04206b4f7c0faf787bdef47c441c5464a0d24f95ea3f9e1c81d5760" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf013f", + "txhash": "a7189cdabf51e9a8930d086450a1f9965f5a2f1bbfcdfb47ba14ec96c5e2ecd0" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf023f", + "txhash": "10f9c1076dc6d34f7177b9366b686a3520af52c8fb5ccb7e3724709ca0210572" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf023f", + "txhash": "08a5646e6638a493aa2814104210bd5f2a543b8b88b379279e5d5a0b0e7c4f3b" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf023f", + "txhash": "113b7e60da52bbd344cd8b18d2fcfdb3f520123e13c8f966a71c161a97c95ba6" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf023f", + "txhash": "387c49d806cb88a1436983b91e2a63653813c24ad4d9974ba8d44c1b63a9557e" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf043f", + "txhash": "b11bc217da71d67ed976f0908471006b0cec02891616c455fcb98d75128bd328" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf043f", + "txhash": "7b3b4b969557627aa398dc10a4e6e9125e7b757abad6dcc06caa3680dea07f70" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf043f", + "txhash": "ac8f492346e55c11add88155404f956536ae1d3ead8df107c253ba50ac0e11b4" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf043f", + "txhash": "407549d8b10ab18bf129363da6c5cc7a3ad21a3bae5068fede793eef4b148d4b" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf083f", + "txhash": "b44795d262703ffab6d38510c419b2a1e42314e1b9cb500ab16db10000f0ca56" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf083f", + "txhash": "d54c373b31de8c13946c34ea4ae27b3af2448dd98b5b901a9816d0454ad615b2" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf083f", + "txhash": "dc37b517a33e8ff426d40178113ffe95cadd5bfeabfe65f449488c7206508b48" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf083f", + "txhash": "546d4464cc1c901ebe5caac760465ac44c6232299417694d5153fa96d0753da6" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf103f", + "txhash": "8ecc06837f33cc467fec862858cb6d7e9c35e1d62a408daf8f6efbab5d812c66" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf103f", + "txhash": "d52c830612571ec9b2dbb6aec9f2aadffd48c76126f34c40fc786ea09c71b0f1" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf103f", + "txhash": "5a42af4f19625d8ec806e8d8e1b0246cdb3059804db50cdf356f52e40b8c86e0" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf103f", + "txhash": "ca2e63bc58e793bcb56fa32fecf3a1ec58df97d53648ebdcdf7f01cd8aa4e63a" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf203f", + "txhash": "c40a940307664a7bba1bf4873d0991f3f0cd4574b04bc89b86dacb12da4b6a21" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf203f", + "txhash": "c1ef990ce8e9d0284d566b35370f01f1c7fff7ab53c7b2a48f5e6e5d78b1c5c7" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf203f", + "txhash": "628072f4eea49cf2c29af02238ca82a91234485ac79307e07d643135409bcfc3" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf203f", + "txhash": "ef1f7b3dc46a2bd6a3d0d6b14568059d246b7337764be277087fdc89424b6b75" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf3f3f", + "txhash": "787817cb8c2ea16d12e2fe66b38f765ba8e170eaddb0c511e9b37f895f5c666f" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf3f3f", + "txhash": "6973cb4041cc8ee9d37b4cdd8e99db9fdb2a131e4ac325924e947315f06701e7" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf3f3f", + "txhash": "cd2bec43e2c4e22b868d0396f0acc9dbe68ce12093e7470b493849d12e174461" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf3f3f", + "txhash": "7c745caed0eec865cc8c8672f4efc45aceb681ee9112508521474848dff12830" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf0180", + "txhash": "ac959dc4847d5fb094be2c6bf9e3ac96d3c1cbc16526876fa69c105dadb3e59d" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf0180", + "txhash": "7519ca17a0305144d817dafc6d9e15c46da39abdd411a6788364fb635e1d7dc4" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf0180", + "txhash": "fb1625c14a7bf1e9bfc26019999992b1512163afd5ac51737f33b643dc6c7ee9" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf0180", + "txhash": "9caa154c3c430a4cda5f693ac58738ab7fd275b0817e87d0096806cc1d5c2aeb" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf0280", + "txhash": "9391fee41bc4e011d6248e350e9810fee89e930425291142675b24712ca65a21" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf0280", + "txhash": "3a615ca685575f58870150c8e3d5fc3d4c869aef55f3a75f8612f1c514fe74a5" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf0280", + "txhash": "1e8aa43768e2f2ac592a42e0a620c012f138cb1f7f889e519edbb64bc683f4fb" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf0280", + "txhash": "876d911b6a58dba83a4b42ad11aaa8bdfc147f6bdccfcb247616762a7ae2ab67" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf0480", + "txhash": "aeed6fe11242904a6b856153c3c5333b37ef93c96d6e301c43325a093a00b174" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf0480", + "txhash": "70fddcb15b6f8b98cb401ca8fb41b5c829980b500b320717b7ab2efb06e99368" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf0480", + "txhash": "f6b7b3bfb91710b07028e282c2d482c2692bf7c4633798aed56bfba5c7cb8a63" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf0480", + "txhash": "9e8ea9ce296035ab4d2602506538976ef157256ff0803a0d388550a6ba16e6c6" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf0880", + "txhash": "f49451b4e7376a60c5b3549e2859ea4ac93399dbcc88ca8fc9e4fbbf0a660d73" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf0880", + "txhash": "d134fa8249145db8226a0aac9b0ae4a63822120cdbfbd9272b675a80cba7ad91" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf0880", + "txhash": "39a0f8a7c74c226c1b14513a9ec2b15446a1ce34b0ab89df56c0852d1bfecffd" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf0880", + "txhash": "beefd026dfcd60543a36a6f7bbffd84f3a78742ea169edca8d03fe9bc849ab6e" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf1080", + "txhash": "0c34aa04360c298392ad1aa7e3ba37c08a714f4941a257bc32fbf60a7429ba10" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf1080", + "txhash": "d0c875772e789e889eac356c7b8f0f353370298d94b03733c10108c5a94544f4" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf1080", + "txhash": "ac0f4560cb2a8ff78d7d3cf32826726c4042ed4c0f5608a57b5a9462d6945fbc" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf1080", + "txhash": "d537146edf56396d3d9102a3d54fbe4345f4ace3f9381c1bbe265e398206ecde" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf2080", + "txhash": "20cb75f623ed737d65149a7524c0dd4b442ccc099f5e9e4baa66d8423519c048" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf2080", + "txhash": "180276f49f05dea354769340c9641bba5d4553b44e394e7178b01606b313960d" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf2080", + "txhash": "6bd0116358cf22d8f2dcc2a318ebb2f74454168df14aee6d468ce5a8a4fb3cd6" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf2080", + "txhash": "23d89f7808eec62ab7135ed2c7103f2660fe64d1e4d917c01a6dc42e314523a8" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf3f80", + "txhash": "b906691aac414c3a31ded419b295c921f6f7bd09e474919fe4341ee26317e08c" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf3f80", + "txhash": "e0daa6996d8a8a808d95787222ffdba42f135fa0b72d64549d040d2fd0ecf9dc" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf3f80", + "txhash": "678472c9fd9a8ffa20d9bff2a3688bb87d509394640b3a5a0e947308f57333fe" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf3f80", + "txhash": "37f06b72cf1f2f577fff579b918bbdd801146fa15d5ba9cf66ac64659369e74e" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf01c0", + "txhash": "703d1a46549f50c8a083d717075f52e7b115b2ee979bf31915fe9a5312c01935" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf01c0", + "txhash": "168074638cc553173d362226045868ba709e4407373dfa51a3db849b45a31b6f" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf01c0", + "txhash": "6efc9bbffa2f4baf83a72c0dad6f927f3e6723d7c66ad6bbd294f783eaf0dbce" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf01c0", + "txhash": "c114c2de48a8390adeaaad5a63e677de18026b934eb0fc4f588e17ef281e1515" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf02c0", + "txhash": "a0aea094bf1fc0266d191eddb13396a02156a97bd9ed73ddd25d71ed18d41e18" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf02c0", + "txhash": "a9a5c7342201f9dc9792825c722336f56ee9fb3b679dbbee2aa603792a4e5a69" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf02c0", + "txhash": "167efe8eedab55a8e1ccc6b2bcc79aa21b3486622b063fc2dea9f32815e182e4" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf02c0", + "txhash": "d4259f2141f0a93411b78d32cc2716bb4a78d5f587df604f90e0cf88a4a0f641" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf04c0", + "txhash": "97495126380c45d14c147ca957fdafd281f43fa5ff9133f8db97f561bba9f276" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf04c0", + "txhash": "5170aa6429978cd526991509af600d3a023f19c91a705ba783f6c815f69195a1" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf04c0", + "txhash": "ec308363d3235813fe12f6ac97d11e806cc9cb4b0922bc09ed394147ec11b3d0" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf04c0", + "txhash": "24328fc176716844102ace005a9fea93177a475d2115d4f9480c3c50ace7ff74" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf08c0", + "txhash": "71a685b55b2fded5618052009c5906ab49b7ddd1e7f4da40d2c44666ea0f7fa1" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf08c0", + "txhash": "71b9f005621870f63372ea8595f58aa9a8cb52fae899df69aa047d18a447ba21" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf08c0", + "txhash": "0d65552788391f605a10e5b21b5f226e2240566a4446a8ab5d52c985aa50ac31" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf08c0", + "txhash": "6440e25776c53da20c47ebfb5beb31956041ea1b1eeb6cf5d88d83a42b5ec550" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf10c0", + "txhash": "d7e6ccec8c6a15c3c7216bbdfa98229413e9e53d006bba5382dd27788ec21c0f" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf10c0", + "txhash": "70be929709d9c3c4c9ef26c4a09ad87ab16199a9273152accd8239dd122a9da4" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf10c0", + "txhash": "8045026a37838ba8caf889ebf4a7d08c350b646a0bca4f76616e5afdc92dd71a" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf10c0", + "txhash": "f56a4095a26dd3578791b2f9bf2e88801c89500f7dc2871137e33317729e8313" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf20c0", + "txhash": "7b5b8cddfb740ab8eee2861ef1a356a2611e23d3d3a4e74c54c8728534cfa393" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf20c0", + "txhash": "96cc12bc1bc76f73e2cf4510e1126bca528db060372e212d4b9b4acecc1993b9" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf20c0", + "txhash": "aca85f5ca541f96e58d286959819e99ab0ba78023e202e40b8fdc8c09d8ec75f" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf20c0", + "txhash": "44b1b71bbda59f87662843921acaf333d303b6e44dee61d47635f86224fbd311" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf3fc0", + "txhash": "e4ad1c14efa4241f07d6e1856130b7be4684a56cb0d5b294e867f14577fafba8" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf3fc0", + "txhash": "b70ab3e74903611697ee4acbd83dd38309668e4a55e4b896fd613980d9dcd862" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf3fc0", + "txhash": "e3d82c6c6de142e2cb4da0af6cd4fd0f33ce9a3189ede385f0512b322561cc70" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf3fc0", + "txhash": "f326187d19bdf2fa2d518170180fec7ceca5972798f6dc315a00a7625e1af332" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf01bf", + "txhash": "2904ae7e8a1a10c7fbd145fe619252e01d7bd21f1787e3afbf70282effe54133" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf01bf", + "txhash": "ab85f02daf2348c5f0128eda15448c82d3f6830dd1266c3659a5b298a1eac7e4" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf01bf", + "txhash": "0ba449340acf50bf5c94a73a902607d93f72b808fc30a3a222173dfa4e22aa25" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf01bf", + "txhash": "cb67d45e1386c92fbc940139093694fca0c3e087465a8e62ddd8a8c0f6269dce" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf02bf", + "txhash": "2a48b62be39251aa84ce4449cd6105aadbdf4e98f37f248439b7365ce4618e56" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf02bf", + "txhash": "6918e003a7098e0d3736da752e520c8cb0798ca0e20a991b987963053308e709" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf02bf", + "txhash": "707fa269766ec96221f2a364648483397d076f6cc1945cc555050f9768fb0d53" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf02bf", + "txhash": "33001865949e90fbb9bd3fb5765aecb4ced05da24c19f995f4edf66173b0d815" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf04bf", + "txhash": "3a4be77c748d9b59ac4a83d8d5f548e446863798bb60fb5876a53dbe2dc81454" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf04bf", + "txhash": "e22d699c81521211137a23aab1d94389822c4342494c89b27942e2991ec4c526" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf04bf", + "txhash": "b2a3644a82ba4021f9ad5d7507c51dcdd2cbac9ba1d7b6ee80739daed2966b44" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf04bf", + "txhash": "96fa865973293dae2846a4b4d4a412cea6fa5adb615d97f1ecf7f77f5c6dcd06" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf08bf", + "txhash": "d09224751853e6ec7cef1cc261bab08c5f697cd430e2b7c6435065ce60e15b3d" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf08bf", + "txhash": "1a71ef9972fac9ed042d6fa9769084601e62a7db270e9af46aa995ed5488aea0" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf08bf", + "txhash": "58610c8a0a6a0392669489c8769b1c0fc65fa5a09bd10f46f7df71f3fc64a58f" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf08bf", + "txhash": "997795369fa0afddce28fd275c0a94c96ddb3047b11d22d3a436cbe85fb0d89b" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf10bf", + "txhash": "8bec5eff66f7de96ccc037c31885f7dae13796a65b692597d64c5754f7c8cf83" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf10bf", + "txhash": "b7b0090b401715af3291333e09884a97624737b5903f774341acfc68ecc253bf" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf10bf", + "txhash": "5b60a5acdbfe9d41cbe7113b23da7ab05eeb9f1483d4bfb3acea5d68fde27a2e" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf10bf", + "txhash": "7b7260a0b97bf6cf51345b436e454038eb2042fb4a821a0606a0f1fc8f639e20" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf20bf", + "txhash": "2334e8398c5b47a357cc223bf8887bbecff1b5d51c3b2b65a5fb8fbfefa5eba2" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf20bf", + "txhash": "f172ad19a8463e0184013bd8f1f0cb3f53676c92018821851bff9a3553ef7a9d" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf20bf", + "txhash": "fcf2e4d9809f85c18d9bd7431b1f28c9a720a2445c1f452e4e4b22802a37e1ec" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf20bf", + "txhash": "e39b0dd76c8f6de2587fc9f37ff5e20a8b61a643826a26c54b0e11ded5eebc54" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "bf3fbf", + "txhash": "704f3966892269f367215ceba260a38f2697b118d43b83bdeb5786e300294397" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "bf3fbf", + "txhash": "f785c544d541edc20c0196e99055f992399e4576d4b3905e401761cb8585bb7c" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "bf3fbf", + "txhash": "bcf596c4f29c79cfdfc347c9a800629c15205b341e2eeae59abf9e18f9785e9c" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "bf3fbf", + "txhash": "e2e1be3fe1ae57fd217b7b1451f2d2ec203b7a01009fb538bd05240c4bb0f792" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffff3f3f", + "txhash": "bd9b6f5c22c7da4c380c239a7ea8b132ada659f5a5334a597e9bd41b78ce1c44" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffff3f3f", + "txhash": "7bcb1faec3b3675f667274924f3f8357fe8af7e9dab1c7ab6662e82f249b30c0" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffff3f3f", + "txhash": "426c94659c2cea2f7761103a0780c78c1935ef6eaec5657080b1326c16849600" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "ffff3f3f", + "txhash": "183229534a28400cb72121911896ed76c3fe5b91fdba12d5b8e36ecd2aff2712" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffff4040", + "txhash": "ecb487752546e951516705c240b5c7d421158b3b1a83ae959d109afd0ce3a208" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffff4040", + "txhash": "78f559915492aff22f62bb24a578825653c473f4ca3ff6d588f2e35de83c676a" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffff4040", + "txhash": "ecab4704755a57b9fc58ad47b6a841e45b9c835721a627b6333d14666b4fe809" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffff8080", + "txhash": "0c1059668b37be3cb601854a16a856642cbc22f41f8e4ec3dad84b6540bcf7d6" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffff8080", + "txhash": "23d5cd006d71efea16c0051035a87c90714568c9d780a0477fe9db3e4331652d" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffff8080", + "txhash": "e129745f8a5c962a3989e62fc471c58c734346f09a41e985dcf4417a974b96a6" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "ffff8080", + "txhash": "40dd74dc56c6bb8436611325d13e15fa7c0d913bd426252e634463b7e993741f" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffffbfbf", + "txhash": "fe0ec98b3d640ec3bec81e0d9b58a7a563a93beb476c61bf648f6c8b2cb3cc4b" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffffbfbf", + "txhash": "44004bc5bf81b4bef13f3b1a88019dd7e8557e8dc7384a4cdb4089714b243025" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffffbfbf", + "txhash": "5b873279c3719485395261e3161b422252e612cd3a083d0c83136be4da4c1c1b" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "ffffbfbf", + "txhash": "244a94e0e8c391b6f105ff2c8da24d26efa67e9b69e1917a17d6ae0c2872b1eb" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffffc0c0", + "txhash": "67ab05d39c6b98b0d8b0ffeb892d6435dcbedb3284dcd7ed4912436a3e7ecee0" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffffc0c0", + "txhash": "667cf47f3d2af2e589135e1fc657bcb2c3008d99c66ae5696f689cdd05987460" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffffc0c0", + "txhash": "0034079e8ee835e01539a90ed12960df0c20d00ce768e5f8cff81f4b1de56718" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffff403f", + "txhash": "de613bc1538f52435e2fcb520d98f5cbfb9922ba6b241c46964ac5a6058bf26d" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffff403f", + "txhash": "ff2e38e426425ac5913674afde5e8113dcacdb32984e93aa31ced26390f2f45c" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffff403f", + "txhash": "cefb9122e27b6faf022b8d5ce8e3ae592b320edd176c4c5394b86ad894553dc4" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "ffff403f", + "txhash": "34535aab9e71af1fe7db821e7e8c5895b2a30b06cf5a03b5974a5d0bebabaea4" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffff3f40", + "txhash": "c593165638d0d7d157c3bdc5295464d6f201c3de660971668c24acb4ff694e1a" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffff3f40", + "txhash": "a91f5fe5a174ce47d5d86c54432b10dc1110db85bc4d6a98243306cb997deb21" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffff3f40", + "txhash": "6e305601b18038736da36ec3b2ac4c8547e5e31c2666ac6e52665435b0cb90a2" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffff0102", + "txhash": "1126186623a5da2b205ce726176fff52cd8ea13d1700fbec2430c6b21fb1947b" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffff0102", + "txhash": "61ae007ce70ce68b324a9f87091e1b9fa3108e93aebb44cef92206ee1610f3d9" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffff0102", + "txhash": "88453084e176f7ea39815b1d971149d1e9eedee019d7073143ee7581f83773aa" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "ffff0102", + "txhash": "30a1e19ef5b0787f1d73fe5f20b849e20861fd950833f300f09018e53d9ded0b" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffff4101420002", + "txhash": "f716067fe48fafd0dc2b8eb7c66bc1e6c5728267679f54c8c6701e512865dc7e" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffff4101420002", + "txhash": "b33949998e8593e81a062494955b43b0efc7d6536f11e3128fb68a1c26e432d5" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffff4101420002", + "txhash": "91f2e9acae60c7550361da5aebd121794f379684541ca3fe0e14b682f2aa9e3d" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "ffff4101420002", + "txhash": "5e60916b38ecc08902ff6edb07a0ca59c788a5b94f2b6cb45dd3473c0c5c6676" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "ffffc101c20002", + "txhash": "3985e4a2cb45548f4f65c1bc19ce4a39b1f377b9846bf9d0b638756511e46519" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "ffffc101c20002", + "txhash": "15b181822828e7dfa1cae09e9e461655f87fc03de4b6c4b338966a397665acad" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "ffffc101c20002", + "txhash": "56e33218cda9d407a8e05939100c90ff32f692b4ab5902dda2852a6be96576da" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "ffffc101c20002", + "txhash": "39e4475f09fbe045174ad7f1bd0db8da8158a542f53380e1e26c06cd098068ea" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "", + "txhash": "723c1c2f5947763d0a3f0d545e5b1d041eb7195037161b65a568ee5977ac486f" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "", + "txhash": "dbb5b4c37596ccfc3ea19931bed2d7f67ccd2362f95cd34e34504b50df724a71" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "", + "txhash": "4cc217e7a101d94c5b54a0442a591933239fb112059ed229d594b8fcee76f3c3" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "", + "txhash": "c64c106fb50f3ba80cf6887cc7161933fc33c658baba57156a7d66b6cd10d35e" + }, + { + "codeseparator": null, + "input": 0, + "txfs": "00", + "txhash": "fe0ec98b3d640ec3bec81e0d9b58a7a563a93beb476c61bf648f6c8b2cb3cc4b" + }, + { + "codeseparator": null, + "input": 1, + "txfs": "00", + "txhash": "44004bc5bf81b4bef13f3b1a88019dd7e8557e8dc7384a4cdb4089714b243025" + }, + { + "codeseparator": null, + "input": 2, + "txfs": "00", + "txhash": "5b873279c3719485395261e3161b422252e612cd3a083d0c83136be4da4c1c1b" + }, + { + "codeseparator": null, + "input": 3, + "txfs": "00", + "txhash": "244a94e0e8c391b6f105ff2c8da24d26efa67e9b69e1917a17d6ae0c2872b1eb" + } + ] + } +] \ No newline at end of file From 52df2b8bf8f5ebf03723c0466874e2c14f8e9e13 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Wed, 20 Mar 2024 20:47:25 +0000 Subject: [PATCH 2/4] txhash: Add relative indices and other improvements --- bip-txhash.md | 210 +++-- bip-txhash/ref-impl/Cargo.toml | 3 +- bip-txhash/ref-impl/src/main.rs | 511 ++++++----- bip-txhash/ref-impl/txhash_vectors.json | 1090 ++++++++++++----------- 4 files changed, 1039 insertions(+), 775 deletions(-) diff --git a/bip-txhash.md b/bip-txhash.md index 7deef3a603..45d8a41384 100644 --- a/bip-txhash.md +++ b/bip-txhash.md @@ -65,51 +65,54 @@ summary, followed by a reference implementation of the CalculateTxHash function. scriptPubkeys. Special case `TXFS_SPECIAL_TEMPLATE` is 4 bytes long, as follows: - *  1. `TXFS_ALL` - *  2. `TXFS_INPUTS_TEMPLATE | TXFS_OUTPUTS_ALL` - *  3. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` - *  4. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + * 1: `TXFS_ALL` + * 2: `TXFS_INPUTS_TEMPLATE | TXFS_OUTPUTS_ALL` + * 3: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + * 4: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` * the `0x00` byte: it is set equal to `TXFS_SPECIAL_ALL`, which means "ALL" and is primarily useful to emulate `SIGHASH_ALL` when `OP_TXHASH` is used in combination with `OP_CHECKSIGFROMSTACK`. Special case `TXFS_SPECIAL_ALL` is 4 bytes long, as follows: - *  1. `TXFS_ALL` - *  2. `TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL` - *  3. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` - *  4. `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + * 1: `TXFS_ALL` + * 2: `TXFS_INPUTS_ALL | TXFS_OUTPUTS_ALL` + * 3: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` + * 4: `TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL` * 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) + * 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. (unused) + * 7: (unused) + * 8: `TXFS_CONTROL` (i.e. include TxFieldSelector into hash) -* The last (highest) bit of the first byte (`TXFS_CONTROL`), we will call the +* 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.) -* If either "inputs" or "outputs" is set to 1, expect another byte with its 8 - bits assigning the following variables, from lowest to highest: +* 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_VALUED`) - *  6. taproot annexes (`TXFS_INPUTS_TAPROOT_ANNEXES`) + * 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_VALUED`) + * 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`) + * 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_INPUTS | TXFS_OUTPUTS | TXFS_CONTROL` @@ -117,59 +120,66 @@ summary, followed by a reference implementation of the CalculateTxHash function. * `TXFS_INPUTS_TEMPLATE = TXFS_INPUTS_SEQUENCES | TXFS_INPUTS_SCRIPTSIGS | TXFS_INPUTS_PREV_VALUES | TXFS_INPUTS_TAPROOT_ANNEXES` * `TXFS_OUTPUTS_ALL = TXFS_OUTPUTS_SCRIPTPUBKEYS | TXFS_OUTPUTS_VALUES` -For both inputs and then outputs, do the following: -* If the "in/outputs" field is set to 1, another additional byte is expected: - * The highest bit (`TXFS_INOUT_NUMBER`) indicates whether the "number of in-/outputs" - should be committed to. +* 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). + * 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_SELECTION_SIZE`) is - used to indicate the "count size", i.e. the number of bytes will be used to + + * 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 remaining lowest 6 bits of the first byte will be - interpreted as `n`, the number of individual in/outputs to select. For each - individual input, (at least) one byte is expected, of this byte. The - highest bit is used to indicate "absolute or relative" indices. - * If the highest bit is set to 0, it is an absolute index. The second - highest bit is used to indicate the amount of bytes are used to represent - the index. - * If the second-highest bit is 0, the remaining 6 bits represent the index to be selected. - * If the second-highest bit is 1, the remaining 6 bits, together with the 8 bits of the next - byte, represent the index to be selected. - * If the highest bit is set to 1, it is a relative index. The second highest bit is used to - indicate the sign of the index. - * If the second-highest bit is set to 0, the remaining 6 bits represent the positive relative - index to be selected. - * If the second-highest bit is set to 1, the remaining 6 bits represent the negative relative - index to be selected. + * 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 8192 -* up to 64 individually selected in/outputs +* 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 -64 to +64. +** using indices relative to the current input index from -8191 to +8192. The TxFieldSelector is invalid when * a byte is expected but missing * additional unexpected bytes are present * index size is set to 1 while not being necessary -* a leading number of individual index is selected out of bounds of the in/outputs +* a leading number or individual index is selected out of bounds of the in/outputs * individual indices are duplicated or not in increasing order +* single relative index of +0, which could be just `TXFS_INOUT_SELECTION_CURRENT` +* input or output fields bits are set, but no inputs or outputs are selected These limitations are to avoid potential TxFieldSelector malleability. It is however allowed to use leading mode where it could be "all". This @@ -178,6 +188,81 @@ is important to allow for optional addition of extra inputs or outputs. //TODO(stevenroose) should we disallow individual that could be leading? +### 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 +| ^ currently unused +^ control bit (ie. include TXFS in hash) +``` + +* second byte + +``` + v 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, @@ -195,9 +280,10 @@ is important to allow for optional addition of extra inputs or outputs. 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 10. If this brings the budget below zero, + decreases the validation budget by 15. 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. + * 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 diff --git a/bip-txhash/ref-impl/Cargo.toml b/bip-txhash/ref-impl/Cargo.toml index e17f8d49c7..05d02e30ff 100644 --- a/bip-txhash/ref-impl/Cargo.toml +++ b/bip-txhash/ref-impl/Cargo.toml @@ -4,11 +4,10 @@ version = "0.0.0" edition = "2021" [dependencies] -bitcoin = { version = "0.31.0", features = [ "serde" ] } +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" } -bitcoin-io = { git = "https://github.com/stevenroose/rust-bitcoin.git", branch = "txhash" } diff --git a/bip-txhash/ref-impl/src/main.rs b/bip-txhash/ref-impl/src/main.rs index a41b0ecb96..c451478a34 100644 --- a/bip-txhash/ref-impl/src/main.rs +++ b/bip-txhash/ref-impl/src/main.rs @@ -6,20 +6,18 @@ 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_CONTROL_BLOCK: u8 = 1 << 3; -pub const TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS: u8 = 1 << 4; -pub const TXFS_INPUTS: u8 = 1 << 5; -pub const TXFS_OUTPUTS: u8 = 1 << 6; - +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_UNUSED: u8 = 1 << 6; pub const TXFS_CONTROL: u8 = 1 << 7; pub const TXFS_ALL: u8 = TXFS_VERSION | TXFS_LOCKTIME | TXFS_CURRENT_INPUT_IDX + | TXFS_CURRENT_INPUT_SPENTSCRIPT | TXFS_CURRENT_INPUT_CONTROL_BLOCK | TXFS_CURRENT_INPUT_LAST_CODESEPARATOR_POS - | TXFS_INPUTS - | TXFS_OUTPUTS | TXFS_CONTROL; pub const TXFS_INPUTS_PREVOUTS: u8 = 1 << 0; @@ -48,9 +46,9 @@ 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_SELECTION_SIZE: u8 = 1 << 5; -pub const TXFS_INOUT_SELECTION_MASK: u8 = - 0xff ^ TXFS_INOUT_NUMBER ^ TXFS_INOUT_SELECTION_MODE ^ TXFS_INOUT_SELECTION_SIZE; +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_ALL: [u8; 4] = [ @@ -68,6 +66,24 @@ pub const TXFS_SPECIAL_TEMPLATE: [u8; 4] = [ 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. @@ -75,14 +91,19 @@ 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 = bytes.next().ok_or("in/output bit set but selection byte missing")?; + 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 { - if !commit_number { - return Err("no in/output selection given and nb_items bitflag also unset"); + if !allow_empty && !commit_number { + return Err("no selection made and also not commiting count"); } vec![] } else if selection == TXFS_INOUT_SELECTION_ALL { @@ -94,7 +115,7 @@ fn parse_inout_selection( } vec![current_input_idx as usize] } else if (selection & TXFS_INOUT_SELECTION_MODE) == 0 { // leading mode - let count = if (selection & TXFS_INOUT_SELECTION_SIZE) == 0 { + let count = if (selection & TXFS_INOUT_LEADING_SIZE) == 0 { (selection & TXFS_INOUT_SELECTION_MASK) as usize } else { if (selection & TXFS_INOUT_SELECTION_MASK) == 0 { @@ -109,6 +130,8 @@ fn parse_inout_selection( } (0..count).collect() } else { // individual mode + let absolute = (selection & TXFS_INOUT_INDIVIDUAL_MODE) == 0; + let count = (selection & TXFS_INOUT_SELECTION_MASK) as usize; if count == 0 { return Err("can't select 0 in/outputs in individual mode"); @@ -116,13 +139,36 @@ fn parse_inout_selection( let mut selected = Vec::with_capacity(count as usize); for _ in 0..count { - let idx = if (selection & TXFS_INOUT_SELECTION_SIZE) == 0 { - bytes.next().ok_or("not enough single-byte indices")? as usize + 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 { + if first == 0 { + return Err("unnecessary two-byte index"); + } + 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 first = bytes.next().ok_or("first byte of two-byte index missing")?; - let second = bytes.next().ok_or("second byte of two-byte index missing")?; - (first as usize) << 8 + (second as usize) + let rel = if single_byte { + read_i7(number as u8) as isize + } else { + read_i15(number as u16) as isize + }; + + if rel == 0 && count == 1 { + return Err("not allowed to only have a single relative index of 0"); + } + 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"); } @@ -164,18 +210,35 @@ pub fn calculate_txhash( engine.input(txfs); } - let mut bytes = txfs.iter().copied(); + 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(&[]) @@ -184,125 +247,128 @@ pub fn calculate_txhash( }; 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(); } - // Stop early if no inputs or outputs are selected. - if (global & TXFS_INPUTS) == 0 && (global & TXFS_OUTPUTS) == 0 { - if txfs.len() > 1 { - return Err("input and output bit unset and more than one byte in txfs"); - } - return Ok(sha256::Hash::from_engine(engine)); - } + 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)), + }; - // Now that we know we have some inputs and/or some outputs to commit. - let inout_fields = bytes.next().ok_or("in- or output bit set but only one byte")?; + if bytes.peek().is_none() { + return Err("in/out field byte set, but no input/outputs selected"); + } - if (global & TXFS_INPUTS) == 0 { - if (inout_fields & TXFS_INPUTS_ALL) != 0 { - return Err("inputs bit not set but some input field bits set"); - } - } else { - let (selection, commit_number) = parse_inout_selection( - &mut bytes, tx.input.len(), current_input_idx, - )?; + // Inputs + let (input_select, commit_number_inputs) = parse_inout_selection( + &mut bytes, tx.input.len(), current_input_idx, true, + )?; - if (inout_fields & TXFS_INPUTS_ALL) == 0 && !selection.is_empty() { - return Err("input selection given but no input field bits set"); - } + if (inout_fields & TXFS_INPUTS_ALL) == 0 && !input_select.is_empty() { + return Err("input selection given but no input field bits set"); + } + if (inout_fields & TXFS_INPUTS_ALL) != 0 && input_select.is_empty() { + return Err("no input selection given but some input field bits set"); + } - if commit_number { - (tx.input.len() as u32).consensus_encode(&mut engine).unwrap(); - } + if commit_number_inputs { + (tx.input.len() as u32).consensus_encode(&mut engine).unwrap(); + } - if !selection.is_empty() && (inout_fields & TXFS_INPUTS_PREVOUTS) != 0 { - let hash = { - let mut engine = sha256::Hash::engine(); - for i in &selection { - 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_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 !selection.is_empty() && (inout_fields & TXFS_INPUTS_SEQUENCES) != 0 { - let hash = { - let mut engine = sha256::Hash::engine(); - for i in &selection { - 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_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 !selection.is_empty() && (inout_fields & TXFS_INPUTS_SCRIPTSIGS) != 0 { - let hash = { - let mut engine = sha256::Hash::engine(); - for i in &selection { - 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_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 !selection.is_empty() && (inout_fields & TXFS_INPUTS_PREV_SCRIPTPUBKEYS) != 0 { - let hash = { - let mut engine = sha256::Hash::engine(); - for i in &selection { - 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_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 !selection.is_empty() && (inout_fields & TXFS_INPUTS_PREV_VALUES) != 0 { - let hash = { - let mut engine = sha256::Hash::engine(); - for i in &selection { - 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_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 !selection.is_empty() && (inout_fields & TXFS_INPUTS_TAPROOT_ANNEXES) != 0 { - let hash = { - let mut engine = sha256::Hash::engine(); - for i in &selection { - 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[..]); - } + 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[..]); - } + } + sha256::Hash::from_engine(engine) + }; + engine.input(&hash[..]); } - if (global & TXFS_OUTPUTS) == 0 { + // Outputs + if bytes.peek().is_none() { if (inout_fields & TXFS_OUTPUTS_ALL) != 0 { - return Err("outputs bit not set but some output field bits set"); + return Err("some output field bits set, but no outputs selected"); } } 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, + &mut bytes, tx.output.len(), current_input_idx, allow_empty, )?; if (inout_fields & TXFS_OUTPUTS_ALL) == 0 && !selection.is_empty() { - return Err("output selection given but no output field bits set"); + return Err("no output field bits set, but output selection provided"); + } + if (inout_fields & TXFS_OUTPUTS_ALL) != 0 && selection.is_empty() { + return Err("output field bits set, but no output selection provided"); } if commit_number { @@ -332,11 +398,14 @@ pub fn calculate_txhash( } } + assert!(bytes.next().is_none(), "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}; @@ -449,122 +518,143 @@ mod test_vectors { } fn generate_vectors() -> Vec { - let selectors: &[&[u8]] = &[ + let all = TXFS_ALL; + 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], - &[1 << 1], - &[1 << 2], - &[1 << 3], - &[1 << 4], - &[0x9f], + (&[1 << 0], r(..)), + (&[1 << 1], r(..)), + (&[1 << 2], r(..)), + (&[1 << 3], r(..)), + (&[1 << 4], r(..)), + (&[0x9f], r(..)), // outputs - &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_SELECTION_CURRENT], - &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_SELECTION_CURRENT], - &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_SELECTION_CURRENT], - &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_SELECTION_ALL], - &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_SELECTION_ALL], - &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_SELECTION_ALL], - &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xdf, TXFS_OUTPUTS_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + (&[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 - &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xbf, TXFS_INPUTS_PREVOUTS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_SEQUENCES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_SCRIPTSIGS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_PREV_SCRIPTPUBKEYS, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_PREV_VALUES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_TAPROOT_ANNEXES, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xbf, TXFS_INPUTS_ALL, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], + (&[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 - &[0xff, 0xff, TXFS_INOUT_SELECTION_ALL, TXFS_INOUT_SELECTION_ALL], - &[0xff, 0xff, TXFS_INOUT_SELECTION_CURRENT, TXFS_INOUT_SELECTION_CURRENT], - &[0xff, 0xff, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE, - TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_NONE], - &[0xff, 0xff, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL, - TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_ALL], - &[0xff, 0xff, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT, - TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_CURRENT], - &[0xff, 0xff, TXFS_INOUT_SELECTION_CURRENT, TXFS_INOUT_SELECTION_ALL], - &[0xff, 0xff, TXFS_INOUT_SELECTION_ALL, TXFS_INOUT_SELECTION_CURRENT], + (&[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 - &[0xff, 0xff, 0x01, 0x02], - // individual - &[0xff, 0xff, TXFS_INOUT_SELECTION_MODE | 0x01, 0x01, - TXFS_INOUT_SELECTION_MODE | 0x02, 0x00, 0x02], - &[0xff, 0xff, TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_MODE | 0x01, 0x01, - TXFS_INOUT_NUMBER | TXFS_INOUT_SELECTION_MODE | 0x02, 0x00, 0x02], + (&[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 cases - &[], - &[0x00], + (&[], r(..)), + (&[0x00], r(..)), ]; let cases = vec![ test_vector_tx(), ]; - let out_selector = |txfs: &[u8]| { - if txfs == &[0x00] || txfs.get(0)? & TXFS_OUTPUTS == 0 { - None - } else if txfs.get(0)? & TXFS_INPUTS == 0 { - Some(txfs[2]) - } else { - Some(txfs[3]) + 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().map(|(tx, prevs)| { + cases.into_iter().enumerate().map(|(cidx, (tx, prevs))| { let mut vectors = Vec::new(); - for txfs in selectors { + for (_sidx, (txfs, idx_range)) in selectors.iter().enumerate() { for i in 0..tx.input.len() { - if i >= tx.output.len() { - if let Some(outs) = out_selector(txfs) { - if (outs & (0xff ^ TXFS_INOUT_NUMBER)) == TXFS_INOUT_SELECTION_CURRENT { - continue; - } - } + // println!("{} >> #{} ({}) >> {}", cidx, _sidx, txfs.as_hex(), i); + if !check_range(idx_range, i) { + continue; } - vectors.push(TestVector { - txfs: txfs.to_vec(), - input: i, - codeseparator: None, - txhash: calculate_txhash(txfs, &tx, &prevs, i as u32, None).unwrap(), - }); + 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 } @@ -574,10 +664,11 @@ mod test_vectors { pub fn write_vector_file(path: impl AsRef) { use bitcoin::consensus::encode::serialize_hex; - let ret = generate_vectors().into_iter().map(|c| serde_json::json!({ + 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().map(|v| serde_json::json!({ + "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, diff --git a/bip-txhash/ref-impl/txhash_vectors.json b/bip-txhash/ref-impl/txhash_vectors.json index b8f91972e0..4d72808471 100644 --- a/bip-txhash/ref-impl/txhash_vectors.json +++ b/bip-txhash/ref-impl/txhash_vectors.json @@ -10,1581 +10,1669 @@ "vectors": [ { "codeseparator": null, + "id": "0:0 (01 #0)", "input": 0, "txfs": "01", "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" }, { "codeseparator": null, + "id": "0:1 (01 #1)", "input": 1, "txfs": "01", "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" }, { "codeseparator": null, + "id": "0:2 (01 #2)", "input": 2, "txfs": "01", "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" }, { "codeseparator": null, + "id": "0:3 (01 #3)", "input": 3, "txfs": "01", "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" }, { "codeseparator": null, + "id": "0:4 (02 #0)", "input": 0, "txfs": "02", "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" }, { "codeseparator": null, + "id": "0:5 (02 #1)", "input": 1, "txfs": "02", "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" }, { "codeseparator": null, + "id": "0:6 (02 #2)", "input": 2, "txfs": "02", "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" }, { "codeseparator": null, + "id": "0:7 (02 #3)", "input": 3, "txfs": "02", "txhash": "e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc" }, { "codeseparator": null, + "id": "0:8 (04 #0)", "input": 0, "txfs": "04", "txhash": "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119" }, { "codeseparator": null, + "id": "0:9 (04 #1)", "input": 1, "txfs": "04", "txhash": "67abdd721024f0ff4e0b3f4c2fc13bc5bad42d0b7851d456d88d203d15aaa450" }, { "codeseparator": null, + "id": "0:10 (04 #2)", "input": 2, "txfs": "04", "txhash": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece" }, { "codeseparator": null, + "id": "0:11 (04 #3)", "input": 3, "txfs": "04", "txhash": "9d9f290527a6be626a8f5985b26e19b237b44872b03631811df4416fc1713178" }, { "codeseparator": null, + "id": "0:12 (08 #0)", "input": 0, "txfs": "08", "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" }, { "codeseparator": null, + "id": "0:13 (08 #1)", "input": 1, "txfs": "08", - "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" + "txhash": "227747766d19539b54f018e7ccfde16bd7c38ebbf5649357ecf67bdfb9755b5c" }, { "codeseparator": null, + "id": "0:14 (08 #2)", "input": 2, "txfs": "08", "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" }, { "codeseparator": null, + "id": "0:15 (08 #3)", "input": 3, "txfs": "08", - "txhash": "d703d3da6a87bd8e0b453f3b6c41edcc9bf331b2b88ef26eb39dc7abee4e00a3" + "txhash": "58b8e1205472ebed51a76303179ebf44554714af49ef1f78fb4c1a6a795aa3d7" }, { "codeseparator": null, + "id": "0:16 (10 #0)", "input": 0, "txfs": "10", - "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" }, { "codeseparator": null, + "id": "0:17 (10 #1)", "input": 1, "txfs": "10", - "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" }, { "codeseparator": null, + "id": "0:18 (10 #2)", "input": 2, "txfs": "10", - "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + "txhash": "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456" }, { "codeseparator": null, + "id": "0:19 (10 #3)", "input": 3, "txfs": "10", - "txhash": "ad95131bc0b799c0b1af477fb14fcf26a6a9f76079e48bf090acb7e8367bfd0e" + "txhash": "d703d3da6a87bd8e0b453f3b6c41edcc9bf331b2b88ef26eb39dc7abee4e00a3" }, { "codeseparator": null, + "id": "0:20 (9f #0)", "input": 0, "txfs": "9f", - "txhash": "4f248b4664d9b7eb78506ee3ae3d40f45b65b2c34f5472bb5e9c367770e7f1ee" + "txhash": "788cda528b3d073edd08e076828d446ba83cf55212e55fd67aae0e3cd26da1cf" }, { "codeseparator": null, + "id": "0:21 (9f #1)", "input": 1, "txfs": "9f", - "txhash": "634565d0889156efa4c3a932c6b832aaeac34af7e77245b1c1db8ed260ad4e1d" + "txhash": "c624f4988c251d9b6867549886a456eddb1fbeb06aba97f45a90fc77239b2802" }, { "codeseparator": null, + "id": "0:22 (9f #2)", "input": 2, "txfs": "9f", - "txhash": "55d54935fbc0dbc572da8ad1eb47c9e3fc28449a070d59a4a2c180f6dc33b285" + "txhash": "381bda8b1a75cd5b6fd36fc99709f12aff9039cf6c05d0506384ca7e1cdebf40" }, { "codeseparator": null, + "id": "0:23 (9f #3)", "input": 3, "txfs": "9f", - "txhash": "51f86e7bb438301882176aa6dff4ba755fed8cf4d2948bf54bcd143bce135376" + "txhash": "a86927ae6acf2887cd752aaa43ece977b08def610f6a8f10aa812b0f92d41ad7" }, { "codeseparator": null, + "id": "0:24 (bf000080 #0)", "input": 0, - "txfs": "df4040", - "txhash": "295da1be3ab5d30bbdbc803ae25066922d0ad964dc08654fc14966d4636271c3" + "txfs": "bf000080", + "txhash": "909743eeacfe89346234ef07de6d565d8cddb2b780feee8f492fecebabebf674" }, { "codeseparator": null, + "id": "0:25 (bf000080 #1)", "input": 1, - "txfs": "df4040", - "txhash": "76c6722f456306260a5c91037dfec2d5bd586ab1f21039216f74455150b221d4" + "txfs": "bf000080", + "txhash": "f0c5621484eae42ee887a3de893de7f8807a3b32773b07716d0207da26661d88" }, { "codeseparator": null, + "id": "0:26 (bf000080 #2)", "input": 2, - "txfs": "df4040", - "txhash": "78cc0e2841fc0c261d98a577ec46af84c0a19ff2d39a9a9e31dec0a5bfab0f09" - }, - { - "codeseparator": null, - "input": 0, - "txfs": "df8040", - "txhash": "563f4ebd7bb6f7f4313eaadab32a1e8b9f949227d0cde6b15712b0f26f25dd03" - }, - { - "codeseparator": null, - "input": 1, - "txfs": "df8040", - "txhash": "1b0b39caf5579978f8ad3542e58a5a5a02e328be0ab357153a7673108a7edf21" + "txfs": "bf000080", + "txhash": "cb43c7e232d8025512b2077df18c8ea4c6f0fc22c427adc7c1f26ba7e0ac72ab" }, { "codeseparator": null, - "input": 2, - "txfs": "df8040", - "txhash": "a145587dce2a85b50bdd085568062612ca3b1ebd4a4ad61f0c765799fd804195" + "id": "0:27 (bf000080 #3)", + "input": 3, + "txfs": "bf000080", + "txhash": "85839bb6f128eec0defa1f46592b9ba6225c496f9221ac1fc6c8474efbf9bb2b" }, { "codeseparator": null, + "id": "0:28 (bf400040 #0)", "input": 0, - "txfs": "dfc040", - "txhash": "7a4f203d00749f677f7cb83043ac4eeb803099063cd01127aeb984bdc4632d7d" + "txfs": "bf400040", + "txhash": "cc2f45fb7ae34a4f536a1fcf2ee42c26aae05a6682d62b8516798b2cc5ad89c3" }, { "codeseparator": null, + "id": "0:29 (bf400040 #1)", "input": 1, - "txfs": "dfc040", - "txhash": "200caf79e16000d8d83cb0d954904ae0780bcae952ac7fa283517e6b8eae69f2" + "txfs": "bf400040", + "txhash": "0545dc92f230c4c1ccd139fad4e35ce58acc5f72a0e6ea052fd9bce36be42a93" }, { "codeseparator": null, + "id": "0:30 (bf400040 #2)", "input": 2, - "txfs": "dfc040", - "txhash": "2cc49b1f27c090bbc7bf1e4e4916b3225a75fdae44be3dadb37b0ee32b1f4499" + "txfs": "bf400040", + "txhash": "e5e7e82c2bc2e4989445937daa9b9fe709c8c200030a266e468577882b667fb7" }, { "codeseparator": null, + "id": "0:31 (bf800040 #0)", "input": 0, - "txfs": "df403f", - "txhash": "5a141494a4ac6a3206fbf0290c094b1aff30638f762f8d15ebea9a1eb98ee309" + "txfs": "bf800040", + "txhash": "838c86478190b2002cb1d70f59f7eb6f9d5e82f8b40a5059a8629ac9f5f2bd0a" }, { "codeseparator": null, + "id": "0:32 (bf800040 #1)", "input": 1, - "txfs": "df403f", - "txhash": "0aaee2fb5fcb20b261346efadbb2165823e24d1e4613ee14396815381172db64" + "txfs": "bf800040", + "txhash": "97d7cd7b16074fff38a7e0e4b401fe837374f0c11bb9fbc73cb15622bddbb0ab" }, { "codeseparator": null, + "id": "0:33 (bf800040 #2)", "input": 2, - "txfs": "df403f", - "txhash": "c51cf8d872616d08f467eade57662b3944b872929c454e71936d315b76683cc6" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "df403f", - "txhash": "36a1135d6bd5585ce45c07813aca090f26acd04501789c0a3c59d5de613f5082" + "txfs": "bf800040", + "txhash": "d2fa000558748e8f5c5ebd210e5128e2ea9090f2be8fc8a12584ee7869d8d0e7" }, { "codeseparator": null, + "id": "0:34 (bfc00040 #0)", "input": 0, - "txfs": "df803f", - "txhash": "4c4364e3e8642aede7454bc332ce5c9973ef6ad6a9ec8817f706f73c9fbfdc24" + "txfs": "bfc00040", + "txhash": "7e7858af5dc394cd72aba8e51e3638d31aec59cad2b370d5d40210d0a0e4d708" }, { "codeseparator": null, + "id": "0:35 (bfc00040 #1)", "input": 1, - "txfs": "df803f", - "txhash": "d619772a10b0fd8f390744a47aba9761deb00798235499253c67e54ce0408b0b" + "txfs": "bfc00040", + "txhash": "0af83972c6f97226ade002e02a70e99bd4f339c702edd8d4b0a74a03a3e15ec8" }, { "codeseparator": null, + "id": "0:36 (bfc00040 #2)", "input": 2, - "txfs": "df803f", - "txhash": "4f2f21a2e853e09daff7540d095f214360ba1172a31ed137ad248229ef9f2df8" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "df803f", - "txhash": "03eb47fa98296ec7ac24c0e91c7fc8f79080af737c7b0c9508aae2a58cb9968e" + "txfs": "bfc00040", + "txhash": "7b664689a999678af52be2c71a89f5900289a8fbf7a8f9c46cee462ea5b7a0b9" }, { "codeseparator": null, + "id": "0:37 (bf40003f #0)", "input": 0, - "txfs": "dfc03f", - "txhash": "49ec992a00d9ddfba9bfe933cf0325a1aa0f97e58382924c6291aca0cb0cc36b" + "txfs": "bf40003f", + "txhash": "5a62d857b3509ae3fdf9380aa34aac830e469f909553535ff0735c180c7a8942" }, { "codeseparator": null, + "id": "0:38 (bf40003f #1)", "input": 1, - "txfs": "dfc03f", - "txhash": "31cb19d1a5d718ad03459183a66e5ea630603ee9efea2219b11e95861561dbb6" + "txfs": "bf40003f", + "txhash": "fd0c43276fb662774cc6bd48736e0cd80b77a9dfd289fce8124f66c4eedc0e16" }, { "codeseparator": null, + "id": "0:39 (bf40003f #2)", "input": 2, - "txfs": "dfc03f", - "txhash": "33862645c5a38f881c35a1b86b56532600be24e3fa6568ec8b97f756192f4a56" + "txfs": "bf40003f", + "txhash": "d060199991a027da98326d292d2fe0ff79f4453fccb2f06843d6aa00f10254b0" }, { "codeseparator": null, + "id": "0:40 (bf40003f #3)", "input": 3, - "txfs": "dfc03f", - "txhash": "8aaa0dd9ed60315a5b55d20e4ca7aa180c5c5f7becaddf93fc43211aa69cdf9e" + "txfs": "bf40003f", + "txhash": "4825fe58227df79896f8d8b7dcb4c6f9d84e33e28f779e51753ac645c7fe4413" }, { "codeseparator": null, + "id": "0:41 (bf80003f #0)", "input": 0, - "txfs": "df4080", - "txhash": "df534a79f3487d041c71ac83e98bd4a8d62b46311ad8521aeab5dce996212697" + "txfs": "bf80003f", + "txhash": "24a648db751ffc8397d9bab0323c1a21056a4ce551e82a8863a2e66d13fd9beb" }, { "codeseparator": null, + "id": "0:42 (bf80003f #1)", "input": 1, - "txfs": "df4080", - "txhash": "160c9bb49915d462f30cd8d73f1ad91790f426781a671add821838a6e29df7df" + "txfs": "bf80003f", + "txhash": "63b2eae798604ee1906a6729d12b2fd2b0fc148cfde58876189359a0642313ad" }, { "codeseparator": null, + "id": "0:43 (bf80003f #2)", "input": 2, - "txfs": "df4080", - "txhash": "5883c4220597e5651c139913c4f4f172ddf03ecb3a4324323e325bb4df7a830d" + "txfs": "bf80003f", + "txhash": "89e12f47147821b36da2580c11e979b71bc174b2e914b22992a8b2b5419fe4be" }, { "codeseparator": null, + "id": "0:44 (bf80003f #3)", "input": 3, - "txfs": "df4080", - "txhash": "0623a9a97b6265f00e498e0e4f9173f6436221f5b7321877f3bf89f7deb1c65a" + "txfs": "bf80003f", + "txhash": "cd084dadb8290ae5ca4340d2cf4c2e4efb6604ed80d6b3456a5d7ef4c30dc48c" }, { "codeseparator": null, + "id": "0:45 (bfc0003f #0)", "input": 0, - "txfs": "df8080", - "txhash": "b3c900a1547c86bbec94d19ed18b6324f79c6945d200fa333ade3ad934efd606" + "txfs": "bfc0003f", + "txhash": "4be49f8fb6648f8a4a9f102402b6cddfd7462e0c825398baed1f83d172c536a3" }, { "codeseparator": null, + "id": "0:46 (bfc0003f #1)", "input": 1, - "txfs": "df8080", - "txhash": "7f9ec8329c6ed349f7f39ce1a3527aaa9cb0a1cfe95201a267db74416dbca620" + "txfs": "bfc0003f", + "txhash": "f9f728de1522010f7c98d3edc1ee981bce965a88d801b31c69e4dc17db62085e" }, { "codeseparator": null, + "id": "0:47 (bfc0003f #2)", "input": 2, - "txfs": "df8080", - "txhash": "abe5b10c15a23ddb09d86fe13108564f08c7cdaf4044eea17697b6c50889af79" + "txfs": "bfc0003f", + "txhash": "026733f329551cd2a878c71f87dfcc5e3356186a740ded3c7e5f173e9a890c9a" }, { "codeseparator": null, + "id": "0:48 (bfc0003f #3)", "input": 3, - "txfs": "df8080", - "txhash": "55102c3467fc5b299def1e1103ae4a59a784200904e755a284335a92d31513df" + "txfs": "bfc0003f", + "txhash": "01fb1414468a6bb0f418f3025c79a2991fb982a2f3cd692fa5a367444b617a05" }, { "codeseparator": null, + "id": "0:49 (bf4000c0 #0)", "input": 0, - "txfs": "dfc080", - "txhash": "086c9be876b432efcc4a82d8b5aa1eea25cdde324552a0c6acce090b45a7f2eb" + "txfs": "bf4000c0", + "txhash": "320ac7fc3f65b98ac24f26c621a525fd6ac40bfdea4642bee57df6b455d4f0bb" }, { "codeseparator": null, + "id": "0:50 (bf4000c0 #1)", "input": 1, - "txfs": "dfc080", - "txhash": "9e1c87642dfe7451423094f60009c689a88efd9c9a2453f8355e0df9d3e2c821" + "txfs": "bf4000c0", + "txhash": "00d5341c01069603bda12f8c774ca750ae4a4943599b4bed80db4ba7fa098c9e" }, { "codeseparator": null, + "id": "0:51 (bf4000c0 #2)", "input": 2, - "txfs": "dfc080", - "txhash": "d14524a4b2de734214a14eed77505dea60ddf90e92d2dee2b4c561f83de70d6c" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "dfc080", - "txhash": "ddc9da80b68d18e7f810668a9e185a4066c0a96c016c510947aabaf2348b5305" + "txfs": "bf4000c0", + "txhash": "bab1c4fdb6e5ec8f0bb212a7ca689f894b32a9131ed87a9f6914062b977ecf9f" }, { "codeseparator": null, + "id": "0:52 (bf8000c0 #0)", "input": 0, - "txfs": "df40c0", - "txhash": "7cabeeea3d2abdfec93ee33c1e7197b8255b342bdfeff200b01f1fa4fdadbe91" + "txfs": "bf8000c0", + "txhash": "24b52d7eccd89a67d6dd53d4c7926b623805b63f89d79ff6ec9c074e4bfd0fca" }, { "codeseparator": null, + "id": "0:53 (bf8000c0 #1)", "input": 1, - "txfs": "df40c0", - "txhash": "83c9657bb3ccf6b5bfd1ee3a38c2fafdeb1d843526cad53aef57a9b6cd13965d" + "txfs": "bf8000c0", + "txhash": "1742194ed59106637bacd13fb741207b79218842f30a56a5bc51fd3ad07690b4" }, { "codeseparator": null, + "id": "0:54 (bf8000c0 #2)", "input": 2, - "txfs": "df40c0", - "txhash": "1343074535405fd18da5e7a78a9ee76dcb0a20c53eb4894c8f577499a2b4ead5" + "txfs": "bf8000c0", + "txhash": "4ef58962ccf9e73e700f220fb36d9efc7453f2a85750fe60e54e8b54103bce06" }, { "codeseparator": null, + "id": "0:55 (bfc000c0 #0)", "input": 0, - "txfs": "df80c0", - "txhash": "3a983b896e2a5a0686fe65cb481d2cb367c80cdb1ab3e3be8baec53237eb88eb" + "txfs": "bfc000c0", + "txhash": "be26ea04357ad58dab384e9056f44ea2fd1215ca45ac6a8e6e074751e10bb8b1" }, { "codeseparator": null, + "id": "0:56 (bfc000c0 #1)", "input": 1, - "txfs": "df80c0", - "txhash": "f7a552eb23e2a4f36fe4b72371e1e2808637f6bc4ea66af789646f404f61ba24" + "txfs": "bfc000c0", + "txhash": "8adca9ef3c5478df8e922fdb07df427be866c81dd781bf62e3f8740a48486ece" }, { "codeseparator": null, + "id": "0:57 (bfc000c0 #2)", "input": 2, - "txfs": "df80c0", - "txhash": "c118fc8b23e2414bfaab1503515529dd5f916c5202dc662e02dcd0f0b6fdaf8a" + "txfs": "bfc000c0", + "txhash": "fa735aa77734ee736a3b87869aab1bc549fee5e6700abe49f4c1d37f21b9b8fb" }, { "codeseparator": null, + "id": "0:58 (bf4000bf #0)", "input": 0, - "txfs": "dfc0c0", - "txhash": "2bd80d3ac416e2bcc2db3aacdf42e8bfa37f0e66d4151e73ce1030560b457eb7" + "txfs": "bf4000bf", + "txhash": "a857fa3a369230319af68871b7708a67011bd6ca2a911a75db952e9f9d038dc7" }, { "codeseparator": null, + "id": "0:59 (bf4000bf #1)", "input": 1, - "txfs": "dfc0c0", - "txhash": "23f3048aaa59b549b4fe9160f0a0ba3d015cb453256b09b588715913dce24d19" + "txfs": "bf4000bf", + "txhash": "29c8b0dcc430e6040ca399420f3af860038c68a6dd815dcacd4d038117e49fb1" }, { "codeseparator": null, + "id": "0:60 (bf4000bf #2)", "input": 2, - "txfs": "dfc0c0", - "txhash": "dd964c815a875dc3abef1c32f790597d23e82cf1c583ab2a1d48f0c0143aab78" + "txfs": "bf4000bf", + "txhash": "03fd5deab0ed07cc4ec63c5467196e43c03d5e9ab4dc37f9990fd1251e61b07a" }, { "codeseparator": null, + "id": "0:61 (bf4000bf #3)", + "input": 3, + "txfs": "bf4000bf", + "txhash": "bd64c8c01ecf01f8f0f6221a16f946c9c237f8a9666049c181860c8b2e349eaa" + }, + { + "codeseparator": null, + "id": "0:62 (bf8000bf #0)", "input": 0, - "txfs": "df40bf", - "txhash": "da59a82b3d3485c4830add922bf465a942c3c9a16c6d3ddde84e79e0975d15ed" + "txfs": "bf8000bf", + "txhash": "94cadfaeb853225d55ab5179661052e98c8ea5b459f35823f0860347feb4ca09" }, { "codeseparator": null, + "id": "0:63 (bf8000bf #1)", "input": 1, - "txfs": "df40bf", - "txhash": "b92124b11d31d9d8e2fe0e0f82ef582b1cd7a59c16ae61a641b8a2ee947314d5" + "txfs": "bf8000bf", + "txhash": "f62e5ce4a5454a7cb2f1dcca724c7db76135a7d63d59408a7563845e3234df9d" }, { "codeseparator": null, + "id": "0:64 (bf8000bf #2)", "input": 2, - "txfs": "df40bf", - "txhash": "79b25aea531bd1138fa6afa1d1f6d4eace1924be8a7a9222503c7613ce5ba0e9" + "txfs": "bf8000bf", + "txhash": "67e24cfb5ffbe4687814804ed65bb425e87da50d6c552ff2387d5a01c17d934a" }, { "codeseparator": null, + "id": "0:65 (bf8000bf #3)", "input": 3, - "txfs": "df40bf", - "txhash": "22eeb3eb26b54f20aa20b94eb9c5eaf47e3989b2681327f81fee9f6a12479dfc" + "txfs": "bf8000bf", + "txhash": "d4d524ad1f8f0def280ad995a96d47b457584dab4f98d0af18c0861fb3ddffaa" }, { "codeseparator": null, + "id": "0:66 (bfc000bf #0)", "input": 0, - "txfs": "df80bf", - "txhash": "acde5987109fbd603ba4fe7f3e539493409daa5c8a80f5b14e071f2729afcdcc" + "txfs": "bfc000bf", + "txhash": "46e767c84c7a5725c0fa2634e2fbe3d639f99fee30740100c825ed15a1c93054" }, { "codeseparator": null, + "id": "0:67 (bfc000bf #1)", "input": 1, - "txfs": "df80bf", - "txhash": "95aa52f89dd3672e2db811a5da70b23d0620e2d7d78ac55746438db53420628e" + "txfs": "bfc000bf", + "txhash": "f4f16538753838051a53dbe298ad0a7b6eed217f4f28e43ebf5e6dd70fc2e9a3" }, { "codeseparator": null, + "id": "0:68 (bfc000bf #2)", "input": 2, - "txfs": "df80bf", - "txhash": "34704e9968c0670f47e97b1fa77e6e441d2cbd97f23aae216654820a97a3119f" + "txfs": "bfc000bf", + "txhash": "e6c44b23e1c89684428649dd7914bb85f1401ea33dd9dcb43d38bff58e4d165c" }, { "codeseparator": null, + "id": "0:69 (bfc000bf #3)", "input": 3, - "txfs": "df80bf", - "txhash": "904338c16b423808e8d3bf491c9ab46308cff6ec09025b20ee288681206133ff" + "txfs": "bfc000bf", + "txhash": "5478d266606667fbb082cf4cc873ac0a19a26fb2e6d1792b1f1efc187996beb1" }, { "codeseparator": null, + "id": "0:70 (bf0080 #0)", "input": 0, - "txfs": "dfc0bf", - "txhash": "d46d78ffaaa4264075c102a2792f35cbd7cb0d2bba46f84a74d3bcc0e2fd4a07" + "txfs": "bf0080", + "txhash": "98099573d0c8acf2412a56d00ccccb3a824bbd338aa2e71015d12ab4e65a308a" }, { "codeseparator": null, + "id": "0:71 (bf0080 #1)", "input": 1, - "txfs": "dfc0bf", - "txhash": "209191f354e31d55cef29adee64c5c70502a6a13d6bf3e93e2cce3cd13d86bde" + "txfs": "bf0080", + "txhash": "11262a337760e5288d3575755164cde0680e98e3d7541d428c2b04f186dc08c0" }, { "codeseparator": null, + "id": "0:72 (bf0080 #2)", "input": 2, - "txfs": "dfc0bf", - "txhash": "a50ecaa5a999547054a44367cdcceb537c75a4b16a2bf43da3f08ef1909725ff" + "txfs": "bf0080", + "txhash": "5ce7cb1c7fb43e49f189912cba730679f7e980d47d6c53237709d3396e20f564" }, { "codeseparator": null, + "id": "0:73 (bf0080 #3)", "input": 3, - "txfs": "dfc0bf", - "txhash": "0c3e7985a168c152f450d39279f9d0a8d8ec0142af05018b8b88e04d3516e73e" + "txfs": "bf0080", + "txhash": "b9ead7c42c8b3f46cd714c7b60a62dc782b88ad70ef19d6cb091d3400b434671" }, { "codeseparator": null, + "id": "0:74 (bf0140 #0)", "input": 0, "txfs": "bf0140", - "txhash": "91d38e0ca0e5095249ddd5c6ea15c98eb09ad84935ee15b078ce16e80062c87b" + "txhash": "5e7f1ccc9daa9a41341c1301f86d9398a5db8276e49c6e080c2ba1fc4def8417" }, { "codeseparator": null, + "id": "0:75 (bf0140 #1)", "input": 1, "txfs": "bf0140", - "txhash": "99e5d133679c749fe3f7a990205aa8b86808bde5d98a563e049eb887def6961d" + "txhash": "49dc0f501b33e22278435c0d67d2c90fadba264ca87d9eef9d1c758efbc8bdab" }, { "codeseparator": null, + "id": "0:76 (bf0140 #2)", "input": 2, "txfs": "bf0140", - "txhash": "fe8a42c47ff81de1664346ba78ac501a7aaf57d56ab5d7edac26220e07420efc" + "txhash": "ddb7d118d80696bfa08dfa5c614d33bf39c6acc597b5b7ef34bf23093bde38db" }, { "codeseparator": null, + "id": "0:77 (bf0140 #3)", "input": 3, "txfs": "bf0140", - "txhash": "e5ec50028b9a3953769c22d5042baf2c0264710467b802d85c8a0967aa80d690" + "txhash": "a71c925f68219cb60a879dc7f2cd5c55ed029077569e46f96e027bafb9004e5c" }, { "codeseparator": null, + "id": "0:78 (bf0240 #0)", "input": 0, "txfs": "bf0240", - "txhash": "ffb8f7ca010f0a1bed083290a7428115ab9a526a6986d169045f987826c3e5a2" + "txhash": "26a5974378ef2d4d876bf25722dd83c40449180a92fd914dc6022e55891a57d8" }, { "codeseparator": null, + "id": "0:79 (bf0240 #1)", "input": 1, "txfs": "bf0240", - "txhash": "56092aa1f0a8b76bd1aa16e8db211d9627f007b9f58a605c16cbd39d3b7d2542" + "txhash": "72eff37a7185242e47f184d3f14ed6d6a3e79643b35d416b78893ed0de4bc8e7" }, { "codeseparator": null, + "id": "0:80 (bf0240 #2)", "input": 2, "txfs": "bf0240", - "txhash": "060bce50807d4bb8662bc9535372d33f649bda3512f4c351b60304e1e3d82ad7" + "txhash": "4712a28e5d7a775679fd0fd092ee807685d276bd31c84a3c7cec7ea01b5d9d61" }, { "codeseparator": null, + "id": "0:81 (bf0240 #3)", "input": 3, "txfs": "bf0240", - "txhash": "4223ea00d094655082bf73cd3fcab91de3b8f8939c56228deee6be429c6c54a5" + "txhash": "55910ced9d5b405fd23456c8ba7cd8726f769d8dbf04b41764eaffa9854e02e9" }, { "codeseparator": null, + "id": "0:82 (bf0440 #0)", "input": 0, "txfs": "bf0440", - "txhash": "9ee281b8f2085859d08cf70696fe023761ac32191767f09ce34179e0a5e21366" + "txhash": "bc0e0f1d960653ba38897859c2d547a214c259811178459c616d301c7e84e193" }, { "codeseparator": null, + "id": "0:83 (bf0440 #1)", "input": 1, "txfs": "bf0440", - "txhash": "c0dc425b65f85631add220410540c860733ec1d0d0e8099e8fdcf8d3b437fd39" + "txhash": "21f224d3a1b46d6dba88629912d881d1f5282c4f0f2159d9972086b20afaa661" }, { "codeseparator": null, + "id": "0:84 (bf0440 #2)", "input": 2, "txfs": "bf0440", - "txhash": "d6d195953a0bd49ccca47f604f04135330420528bf9a542fab16fa2e7b582568" + "txhash": "a81748740b6ee92fe80c18bcb861bcd760644d189e32a2ebbb38806c1fbc7a88" }, { "codeseparator": null, + "id": "0:85 (bf0440 #3)", "input": 3, "txfs": "bf0440", - "txhash": "a34f814e1c1ac0a9f429cd48675c26d89bc23d3e358a3343734423445a76a890" + "txhash": "dec9e7680c658395ca2b70c77d072fbe79f90d49d147497d77983d46dd6cfa69" }, { "codeseparator": null, + "id": "0:86 (bf0840 #0)", "input": 0, "txfs": "bf0840", - "txhash": "112c0da280dd4c137c7aee304d3171b0a28b9fa218cc869570b2916a579a15a3" + "txhash": "2c63aeb94ce9efe3274c6b627d3b3aece1a63fafd88fe166891fc0489a28921d" }, { "codeseparator": null, + "id": "0:87 (bf0840 #1)", "input": 1, "txfs": "bf0840", - "txhash": "54244057189beb0ee25325b4f2d2814a471b04de0a5a0906c5b0b56d97444f43" + "txhash": "c50717ebf3809b46a1c6699187cc63f3f0e3623e1740d44a08aab35102482bc4" }, { "codeseparator": null, + "id": "0:88 (bf0840 #2)", "input": 2, "txfs": "bf0840", - "txhash": "0c136aab4e6a317299df526a51714951741b32ff8d4caf5cb3d16984b5c9d7e0" + "txhash": "b3fdfb77fb2753960369edf1cd2760cceb431dc7c358228c304d142061f05312" }, { "codeseparator": null, + "id": "0:89 (bf0840 #3)", "input": 3, "txfs": "bf0840", - "txhash": "4e520f2941db8d101b9d96a7899796954df87f0ec09944a77c1b4bfb332809c2" + "txhash": "69a3b36bfb519c5cbdafb7b9d1a2617a5b190b5bcf7afbb479ff0a392e0a7349" }, { "codeseparator": null, + "id": "0:90 (bf1040 #0)", "input": 0, "txfs": "bf1040", - "txhash": "dbd2f02312c7442ab580a8bb45711688f5c1142555fd8c94dac76f72925b1aff" + "txhash": "103fde157d4f8a39a8a9fdf53df1369b8ad67e55d0058300e4252ee2f939d8bd" }, { "codeseparator": null, + "id": "0:91 (bf1040 #1)", "input": 1, "txfs": "bf1040", - "txhash": "5073580ef20d94a230bfdfe3f4d850a0d4a405957e46262ec2c02d3c53ce6d00" + "txhash": "7c5b6a65ef53f14abc2c3cb824ee138062d31f27fc3f064d4125f80ab2877579" }, { "codeseparator": null, + "id": "0:92 (bf1040 #2)", "input": 2, "txfs": "bf1040", - "txhash": "e2aae3653e626a6f56e5901d44de6c8a2f65fcdaaa21552ad4a9b3263cfbf393" + "txhash": "c71655fa2fe5e8f97edfcb8d0a610d252c2fe029efc64bca1b022bcdd371c460" }, { "codeseparator": null, + "id": "0:93 (bf1040 #3)", "input": 3, "txfs": "bf1040", - "txhash": "cfdc04bd6c444eb9bb0e2db32db7bb66542c7ae5f99d831ed899d50a7f3ccb11" + "txhash": "be78626e6af36f7e13dd19e979dec9f7bafaf1f942f430f331028120f511fbba" }, { "codeseparator": null, + "id": "0:94 (bf2040 #0)", "input": 0, "txfs": "bf2040", - "txhash": "5144500234219dac2bfd9f1bb8a92886e5f8b143e009341936a12a6fb26c750a" + "txhash": "8b8a9c11cb01fcac02d2692741df9075bb82932e098b05930a91e46070b5a2b8" }, { "codeseparator": null, + "id": "0:95 (bf2040 #1)", "input": 1, "txfs": "bf2040", - "txhash": "1adbc6c62633fd18c1d0570d3c8a8f9bb0ea073aa8a79995a9cfed622a4608a4" + "txhash": "3a690a5dca6a070769a50c186332102978ddbdff52fcb82fe7a33a74400add31" }, { "codeseparator": null, + "id": "0:96 (bf2040 #2)", "input": 2, "txfs": "bf2040", - "txhash": "7c2c214489c1496236c71cf5a47c77979b81604dd0b1a6aa4eabcb867dfd3bae" + "txhash": "08e06a10f99abf7f53b2ea5e0b20f993787420c3d6871217b7e583530e5f32b9" }, { "codeseparator": null, + "id": "0:97 (bf2040 #3)", "input": 3, "txfs": "bf2040", - "txhash": "9095453a420b8f59a62368eee24769741241c15aa99e74cbf8ce029b28a5b8bc" + "txhash": "b6529d7943d188e82a5fd1512c93c5fd6ac0252f79098d3cb04d471a5ccdb452" }, { "codeseparator": null, + "id": "0:98 (bf3f40 #0)", "input": 0, "txfs": "bf3f40", - "txhash": "4b1e78f5da41122f5155ba68146cf5100baa90a3e92e44009eb18ec8c2c11e55" + "txhash": "62b46938532bb50b7b8dfa19f155c2066cc4f8138ab7ccbb4f7cb643bd745ae0" }, { "codeseparator": null, + "id": "0:99 (bf3f40 #1)", "input": 1, "txfs": "bf3f40", - "txhash": "58db27da765fca79e76f2d5b45b8a215e8e2dfd57f3e624d5ba700150ff72a10" + "txhash": "c69f90f330d6729f654e1cceddb5f852468ceab164772c3d985c6ecde6849899" }, { "codeseparator": null, + "id": "0:100 (bf3f40 #2)", "input": 2, "txfs": "bf3f40", - "txhash": "32f66cc966a04e69dd72b9ffb1dbf58edc7b1d51fde4084cee0dc8000374fda5" + "txhash": "b50fe6e1494b7581ab62f74495371e1856122e643baf002fe83101ae54c2e667" }, { "codeseparator": null, + "id": "0:101 (bf3f40 #3)", "input": 3, "txfs": "bf3f40", - "txhash": "1dd603250c852348e98057583c81fb31bcdf4918a7023256b047f5acd46d81ee" + "txhash": "405a5f91daaefe617a83efd00482bdc2219620df7507b9f146f8b5ba028946b1" }, { "codeseparator": null, + "id": "0:102 (bf013f #0)", "input": 0, "txfs": "bf013f", - "txhash": "c133c0a4d74c5736ef471afc33d8cc7aa91ecfea07509458f127e6fde2400676" + "txhash": "a10db52c586a6aee9e6102b220726777fa5bbfcd410949bceef034364a3acdff" }, { "codeseparator": null, + "id": "0:103 (bf013f #1)", "input": 1, "txfs": "bf013f", - "txhash": "704cdcaffd4c954061ec58b872fbee9c2b94b7385de9e11b5b5d1e43e888e510" + "txhash": "eb0206e898a977096ee95283e4ebc5812559652fdc2ae3ac024d69cd553eea87" }, { "codeseparator": null, + "id": "0:104 (bf013f #2)", "input": 2, "txfs": "bf013f", - "txhash": "cc02ac60a04206b4f7c0faf787bdef47c441c5464a0d24f95ea3f9e1c81d5760" + "txhash": "29d20f8ebd591394c41abcac676d7ae0e6d8985201d00004cf2705ff8071bc1a" }, { "codeseparator": null, + "id": "0:105 (bf013f #3)", "input": 3, "txfs": "bf013f", - "txhash": "a7189cdabf51e9a8930d086450a1f9965f5a2f1bbfcdfb47ba14ec96c5e2ecd0" + "txhash": "88f4156cb13e8b42a551dbdaa943d2b489c637f414207dca61a52077cbe4324f" }, { "codeseparator": null, + "id": "0:106 (bf023f #0)", "input": 0, "txfs": "bf023f", - "txhash": "10f9c1076dc6d34f7177b9366b686a3520af52c8fb5ccb7e3724709ca0210572" + "txhash": "9d6ae6a8acae0d6f103ebc84aeb782d16de96d31619b64f7c997fbe52fe8c437" }, { "codeseparator": null, + "id": "0:107 (bf023f #1)", "input": 1, "txfs": "bf023f", - "txhash": "08a5646e6638a493aa2814104210bd5f2a543b8b88b379279e5d5a0b0e7c4f3b" + "txhash": "4eae0eea63bb071811b0d7db65ffdb21befada00f3c7b63bcf966b462b5d6a6c" }, { "codeseparator": null, + "id": "0:108 (bf023f #2)", "input": 2, "txfs": "bf023f", - "txhash": "113b7e60da52bbd344cd8b18d2fcfdb3f520123e13c8f966a71c161a97c95ba6" + "txhash": "a422efd98ec2f6b3d99ce3fbb2b333bc56914f1b9800e4c7b6865aec9de3ac8c" }, { "codeseparator": null, + "id": "0:109 (bf023f #3)", "input": 3, "txfs": "bf023f", - "txhash": "387c49d806cb88a1436983b91e2a63653813c24ad4d9974ba8d44c1b63a9557e" + "txhash": "3ed241395d201889d5038303451be81961af39ce22458c1b330f39511742870e" }, { "codeseparator": null, + "id": "0:110 (bf043f #0)", "input": 0, "txfs": "bf043f", - "txhash": "b11bc217da71d67ed976f0908471006b0cec02891616c455fcb98d75128bd328" + "txhash": "b5690bbdf246eed4e9637035446d9ce3e70506c6da4df81d1902f8696e20302e" }, { "codeseparator": null, + "id": "0:111 (bf043f #1)", "input": 1, "txfs": "bf043f", - "txhash": "7b3b4b969557627aa398dc10a4e6e9125e7b757abad6dcc06caa3680dea07f70" + "txhash": "1c5ba6240885fc6ac83d51eb9dc2b1e65866e12df1a486bb43e9bb8a4913fdde" }, { "codeseparator": null, + "id": "0:112 (bf043f #2)", "input": 2, "txfs": "bf043f", - "txhash": "ac8f492346e55c11add88155404f956536ae1d3ead8df107c253ba50ac0e11b4" + "txhash": "373d9ad97ac012472d6c9ef12cf9572f309e81fc4274615f3959b8bf68bace2e" }, { "codeseparator": null, + "id": "0:113 (bf043f #3)", "input": 3, "txfs": "bf043f", - "txhash": "407549d8b10ab18bf129363da6c5cc7a3ad21a3bae5068fede793eef4b148d4b" + "txhash": "c0ad2b14dca838cd9f49c41ec61b08edd6c32c400dfe707377b4f0a1b540ac73" }, { "codeseparator": null, + "id": "0:114 (bf083f #0)", "input": 0, "txfs": "bf083f", - "txhash": "b44795d262703ffab6d38510c419b2a1e42314e1b9cb500ab16db10000f0ca56" + "txhash": "df10b44dd8bb7eea80704941d96e8dcda57a4237c385e6afef129313020ea4eb" }, { "codeseparator": null, + "id": "0:115 (bf083f #1)", "input": 1, "txfs": "bf083f", - "txhash": "d54c373b31de8c13946c34ea4ae27b3af2448dd98b5b901a9816d0454ad615b2" + "txhash": "ea971784d31c7bfe4bfb8c9eaf4a79d64ca281086c2120b41ed0992faef7d015" }, { "codeseparator": null, + "id": "0:116 (bf083f #2)", "input": 2, "txfs": "bf083f", - "txhash": "dc37b517a33e8ff426d40178113ffe95cadd5bfeabfe65f449488c7206508b48" + "txhash": "ad45cf7e636e2157e7ea0946785e4ae4f06a194bd78675e9f164e0d23776095b" }, { "codeseparator": null, + "id": "0:117 (bf083f #3)", "input": 3, "txfs": "bf083f", - "txhash": "546d4464cc1c901ebe5caac760465ac44c6232299417694d5153fa96d0753da6" + "txhash": "2cd38bb04853b7b5ead4bc6c2ccd45d8565d3bf366c360602f681c82f1473834" }, { "codeseparator": null, + "id": "0:118 (bf103f #0)", "input": 0, "txfs": "bf103f", - "txhash": "8ecc06837f33cc467fec862858cb6d7e9c35e1d62a408daf8f6efbab5d812c66" + "txhash": "d3d206eb5bfcaad0c1f7ea33f7d17bfd5b51c82ab104de00c2bc968a03fe2f1c" }, { "codeseparator": null, + "id": "0:119 (bf103f #1)", "input": 1, "txfs": "bf103f", - "txhash": "d52c830612571ec9b2dbb6aec9f2aadffd48c76126f34c40fc786ea09c71b0f1" + "txhash": "832701794b4cae9afec64b5fda02c1e06a89de7539c4ccd70c32db19c4420a05" }, { "codeseparator": null, + "id": "0:120 (bf103f #2)", "input": 2, "txfs": "bf103f", - "txhash": "5a42af4f19625d8ec806e8d8e1b0246cdb3059804db50cdf356f52e40b8c86e0" + "txhash": "c2dfa9f5c46d1a2e7fecf7bfd9495e494b0f55aa99aa02beeab0eeadba2c243d" }, { "codeseparator": null, + "id": "0:121 (bf103f #3)", "input": 3, "txfs": "bf103f", - "txhash": "ca2e63bc58e793bcb56fa32fecf3a1ec58df97d53648ebdcdf7f01cd8aa4e63a" + "txhash": "ce5973996dee00456d723e39e63beeeaf2dd1266a0744bed81120ab002e8401c" }, { "codeseparator": null, + "id": "0:122 (bf203f #0)", "input": 0, "txfs": "bf203f", - "txhash": "c40a940307664a7bba1bf4873d0991f3f0cd4574b04bc89b86dacb12da4b6a21" + "txhash": "fb371243b7f09a6d0cdf60f8e7c94375f5b25ae47a5090f0a310de977ae3fc94" }, { "codeseparator": null, + "id": "0:123 (bf203f #1)", "input": 1, "txfs": "bf203f", - "txhash": "c1ef990ce8e9d0284d566b35370f01f1c7fff7ab53c7b2a48f5e6e5d78b1c5c7" + "txhash": "5112347c861514ae312130ed00ec7d7ac4febc5bc1e1c8e665568d02e24bbe89" }, { "codeseparator": null, + "id": "0:124 (bf203f #2)", "input": 2, "txfs": "bf203f", - "txhash": "628072f4eea49cf2c29af02238ca82a91234485ac79307e07d643135409bcfc3" + "txhash": "d3a4b8d449861b7391b084e1156fedd4402c8e6e6892e5f80cb4519bdf404ce3" }, { "codeseparator": null, + "id": "0:125 (bf203f #3)", "input": 3, "txfs": "bf203f", - "txhash": "ef1f7b3dc46a2bd6a3d0d6b14568059d246b7337764be277087fdc89424b6b75" + "txhash": "ca878a865f411e4a0975b9e825877bd66a3ee1ca31c1bb68277aacd4763b6c37" }, { "codeseparator": null, + "id": "0:126 (bf3f3f #0)", "input": 0, "txfs": "bf3f3f", - "txhash": "787817cb8c2ea16d12e2fe66b38f765ba8e170eaddb0c511e9b37f895f5c666f" + "txhash": "5110b06a0372f39037991c5c397be5f51917650022689cc127a0c344e9aea025" }, { "codeseparator": null, + "id": "0:127 (bf3f3f #1)", "input": 1, "txfs": "bf3f3f", - "txhash": "6973cb4041cc8ee9d37b4cdd8e99db9fdb2a131e4ac325924e947315f06701e7" + "txhash": "31c960f9b2d9ee892043ee348917e78d50e87c42e3a199792ae5b43cb17063c9" }, { "codeseparator": null, + "id": "0:128 (bf3f3f #2)", "input": 2, "txfs": "bf3f3f", - "txhash": "cd2bec43e2c4e22b868d0396f0acc9dbe68ce12093e7470b493849d12e174461" + "txhash": "29931e33cfdf36fc120990f21528d8000c847cf2d0d6b290c9a2a90aaa559827" }, { "codeseparator": null, + "id": "0:129 (bf3f3f #3)", "input": 3, "txfs": "bf3f3f", - "txhash": "7c745caed0eec865cc8c8672f4efc45aceb681ee9112508521474848dff12830" - }, - { - "codeseparator": null, - "input": 0, - "txfs": "bf0180", - "txhash": "ac959dc4847d5fb094be2c6bf9e3ac96d3c1cbc16526876fa69c105dadb3e59d" - }, - { - "codeseparator": null, - "input": 1, - "txfs": "bf0180", - "txhash": "7519ca17a0305144d817dafc6d9e15c46da39abdd411a6788364fb635e1d7dc4" - }, - { - "codeseparator": null, - "input": 2, - "txfs": "bf0180", - "txhash": "fb1625c14a7bf1e9bfc26019999992b1512163afd5ac51737f33b643dc6c7ee9" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "bf0180", - "txhash": "9caa154c3c430a4cda5f693ac58738ab7fd275b0817e87d0096806cc1d5c2aeb" - }, - { - "codeseparator": null, - "input": 0, - "txfs": "bf0280", - "txhash": "9391fee41bc4e011d6248e350e9810fee89e930425291142675b24712ca65a21" - }, - { - "codeseparator": null, - "input": 1, - "txfs": "bf0280", - "txhash": "3a615ca685575f58870150c8e3d5fc3d4c869aef55f3a75f8612f1c514fe74a5" - }, - { - "codeseparator": null, - "input": 2, - "txfs": "bf0280", - "txhash": "1e8aa43768e2f2ac592a42e0a620c012f138cb1f7f889e519edbb64bc683f4fb" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "bf0280", - "txhash": "876d911b6a58dba83a4b42ad11aaa8bdfc147f6bdccfcb247616762a7ae2ab67" - }, - { - "codeseparator": null, - "input": 0, - "txfs": "bf0480", - "txhash": "aeed6fe11242904a6b856153c3c5333b37ef93c96d6e301c43325a093a00b174" - }, - { - "codeseparator": null, - "input": 1, - "txfs": "bf0480", - "txhash": "70fddcb15b6f8b98cb401ca8fb41b5c829980b500b320717b7ab2efb06e99368" - }, - { - "codeseparator": null, - "input": 2, - "txfs": "bf0480", - "txhash": "f6b7b3bfb91710b07028e282c2d482c2692bf7c4633798aed56bfba5c7cb8a63" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "bf0480", - "txhash": "9e8ea9ce296035ab4d2602506538976ef157256ff0803a0d388550a6ba16e6c6" - }, - { - "codeseparator": null, - "input": 0, - "txfs": "bf0880", - "txhash": "f49451b4e7376a60c5b3549e2859ea4ac93399dbcc88ca8fc9e4fbbf0a660d73" - }, - { - "codeseparator": null, - "input": 1, - "txfs": "bf0880", - "txhash": "d134fa8249145db8226a0aac9b0ae4a63822120cdbfbd9272b675a80cba7ad91" - }, - { - "codeseparator": null, - "input": 2, - "txfs": "bf0880", - "txhash": "39a0f8a7c74c226c1b14513a9ec2b15446a1ce34b0ab89df56c0852d1bfecffd" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "bf0880", - "txhash": "beefd026dfcd60543a36a6f7bbffd84f3a78742ea169edca8d03fe9bc849ab6e" - }, - { - "codeseparator": null, - "input": 0, - "txfs": "bf1080", - "txhash": "0c34aa04360c298392ad1aa7e3ba37c08a714f4941a257bc32fbf60a7429ba10" - }, - { - "codeseparator": null, - "input": 1, - "txfs": "bf1080", - "txhash": "d0c875772e789e889eac356c7b8f0f353370298d94b03733c10108c5a94544f4" - }, - { - "codeseparator": null, - "input": 2, - "txfs": "bf1080", - "txhash": "ac0f4560cb2a8ff78d7d3cf32826726c4042ed4c0f5608a57b5a9462d6945fbc" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "bf1080", - "txhash": "d537146edf56396d3d9102a3d54fbe4345f4ace3f9381c1bbe265e398206ecde" - }, - { - "codeseparator": null, - "input": 0, - "txfs": "bf2080", - "txhash": "20cb75f623ed737d65149a7524c0dd4b442ccc099f5e9e4baa66d8423519c048" - }, - { - "codeseparator": null, - "input": 1, - "txfs": "bf2080", - "txhash": "180276f49f05dea354769340c9641bba5d4553b44e394e7178b01606b313960d" - }, - { - "codeseparator": null, - "input": 2, - "txfs": "bf2080", - "txhash": "6bd0116358cf22d8f2dcc2a318ebb2f74454168df14aee6d468ce5a8a4fb3cd6" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "bf2080", - "txhash": "23d89f7808eec62ab7135ed2c7103f2660fe64d1e4d917c01a6dc42e314523a8" - }, - { - "codeseparator": null, - "input": 0, - "txfs": "bf3f80", - "txhash": "b906691aac414c3a31ded419b295c921f6f7bd09e474919fe4341ee26317e08c" - }, - { - "codeseparator": null, - "input": 1, - "txfs": "bf3f80", - "txhash": "e0daa6996d8a8a808d95787222ffdba42f135fa0b72d64549d040d2fd0ecf9dc" - }, - { - "codeseparator": null, - "input": 2, - "txfs": "bf3f80", - "txhash": "678472c9fd9a8ffa20d9bff2a3688bb87d509394640b3a5a0e947308f57333fe" - }, - { - "codeseparator": null, - "input": 3, - "txfs": "bf3f80", - "txhash": "37f06b72cf1f2f577fff579b918bbdd801146fa15d5ba9cf66ac64659369e74e" + "txhash": "70526710c2a21a8cdac91e71927477a3340af535cfd2ac78ac440525d67f08a3" }, { "codeseparator": null, + "id": "0:130 (bf01c0 #0)", "input": 0, "txfs": "bf01c0", - "txhash": "703d1a46549f50c8a083d717075f52e7b115b2ee979bf31915fe9a5312c01935" + "txhash": "e27f8403f4962e7324799a6ab6d13793c6fbd9b1dcd1abb6e1ec58de74eb9dab" }, { "codeseparator": null, + "id": "0:131 (bf01c0 #1)", "input": 1, "txfs": "bf01c0", - "txhash": "168074638cc553173d362226045868ba709e4407373dfa51a3db849b45a31b6f" + "txhash": "8569d6fc9a0df3eb0fa19716da6a0e1862d557848f3cc831cc950468c6e025ad" }, { "codeseparator": null, + "id": "0:132 (bf01c0 #2)", "input": 2, "txfs": "bf01c0", - "txhash": "6efc9bbffa2f4baf83a72c0dad6f927f3e6723d7c66ad6bbd294f783eaf0dbce" + "txhash": "48b2978794a8d67f1aa905a57ee192c6d171c2b11e863b5ff4383ef6f3885b01" }, { "codeseparator": null, + "id": "0:133 (bf01c0 #3)", "input": 3, "txfs": "bf01c0", - "txhash": "c114c2de48a8390adeaaad5a63e677de18026b934eb0fc4f588e17ef281e1515" + "txhash": "f4c2b7d35210255d398d0a0bc140b2a2764e88a8a45fa49303079d69773b61a8" }, { "codeseparator": null, + "id": "0:134 (bf02c0 #0)", "input": 0, "txfs": "bf02c0", - "txhash": "a0aea094bf1fc0266d191eddb13396a02156a97bd9ed73ddd25d71ed18d41e18" + "txhash": "64b349c2b6a89f517a2d8b2de32648ecd1d9abada58db2c1d583d7d565135dad" }, { "codeseparator": null, + "id": "0:135 (bf02c0 #1)", "input": 1, "txfs": "bf02c0", - "txhash": "a9a5c7342201f9dc9792825c722336f56ee9fb3b679dbbee2aa603792a4e5a69" + "txhash": "531ae40c0a98ee56dc5bf388023254c6ed8c267e8b13531b629b7da721723b1e" }, { "codeseparator": null, + "id": "0:136 (bf02c0 #2)", "input": 2, "txfs": "bf02c0", - "txhash": "167efe8eedab55a8e1ccc6b2bcc79aa21b3486622b063fc2dea9f32815e182e4" + "txhash": "cdff9a58a95eaca822ae13198bde7df62468194b287e1fc63825f3beab54617e" }, { "codeseparator": null, + "id": "0:137 (bf02c0 #3)", "input": 3, "txfs": "bf02c0", - "txhash": "d4259f2141f0a93411b78d32cc2716bb4a78d5f587df604f90e0cf88a4a0f641" + "txhash": "ef374acb1c23ffff8102294a8e289199bf8948ab18d52d43756d127fd29081e4" }, { "codeseparator": null, + "id": "0:138 (bf04c0 #0)", "input": 0, "txfs": "bf04c0", - "txhash": "97495126380c45d14c147ca957fdafd281f43fa5ff9133f8db97f561bba9f276" + "txhash": "f644362b941e62dbdd044c3b8bd7ab5ef494efafb99a1f05a04b02445d448ea1" }, { "codeseparator": null, + "id": "0:139 (bf04c0 #1)", "input": 1, "txfs": "bf04c0", - "txhash": "5170aa6429978cd526991509af600d3a023f19c91a705ba783f6c815f69195a1" + "txhash": "88b716e88fa0f9e12b4c8556f29625c7352c946c78cf342ddb5588fa9287126d" }, { "codeseparator": null, + "id": "0:140 (bf04c0 #2)", "input": 2, "txfs": "bf04c0", - "txhash": "ec308363d3235813fe12f6ac97d11e806cc9cb4b0922bc09ed394147ec11b3d0" + "txhash": "188e7b1ab08e26959d5738989bbaa4c50e8010fe031853a6d348ebac768ad447" }, { "codeseparator": null, + "id": "0:141 (bf04c0 #3)", "input": 3, "txfs": "bf04c0", - "txhash": "24328fc176716844102ace005a9fea93177a475d2115d4f9480c3c50ace7ff74" + "txhash": "abe4b758dec4b8deb33327f12de2fb60905eb2750584ae3e81e641a359542a85" }, { "codeseparator": null, + "id": "0:142 (bf08c0 #0)", "input": 0, "txfs": "bf08c0", - "txhash": "71a685b55b2fded5618052009c5906ab49b7ddd1e7f4da40d2c44666ea0f7fa1" + "txhash": "273358e4201e28721f9c8408eae617fe8919eb3b815c909e50863258311c3338" }, { "codeseparator": null, + "id": "0:143 (bf08c0 #1)", "input": 1, "txfs": "bf08c0", - "txhash": "71b9f005621870f63372ea8595f58aa9a8cb52fae899df69aa047d18a447ba21" + "txhash": "3d4f4e9fb10b24194dbeea548ee0c803e430b8ece32fd6da1769e8fde4b1e38e" }, { "codeseparator": null, + "id": "0:144 (bf08c0 #2)", "input": 2, "txfs": "bf08c0", - "txhash": "0d65552788391f605a10e5b21b5f226e2240566a4446a8ab5d52c985aa50ac31" + "txhash": "9346f4afd1b08b945effb9db026ab658c1ba3e4e6785433ecba957b491601cee" }, { "codeseparator": null, + "id": "0:145 (bf08c0 #3)", "input": 3, "txfs": "bf08c0", - "txhash": "6440e25776c53da20c47ebfb5beb31956041ea1b1eeb6cf5d88d83a42b5ec550" + "txhash": "e083483af63a4b8cb9498b192e660852ee28a1a5170f31d48b4fefb998df3119" }, { "codeseparator": null, + "id": "0:146 (bf10c0 #0)", "input": 0, "txfs": "bf10c0", - "txhash": "d7e6ccec8c6a15c3c7216bbdfa98229413e9e53d006bba5382dd27788ec21c0f" + "txhash": "201faca1e41f7095decf06f94f2132755874cc7b918c1768eba1de9ea507cd7c" }, { "codeseparator": null, + "id": "0:147 (bf10c0 #1)", "input": 1, "txfs": "bf10c0", - "txhash": "70be929709d9c3c4c9ef26c4a09ad87ab16199a9273152accd8239dd122a9da4" + "txhash": "46a41197c242b275e780cf322b4b2ccf6790d928a110461bd4077db6492c2283" }, { "codeseparator": null, + "id": "0:148 (bf10c0 #2)", "input": 2, "txfs": "bf10c0", - "txhash": "8045026a37838ba8caf889ebf4a7d08c350b646a0bca4f76616e5afdc92dd71a" + "txhash": "759a5cf343b3e47a2b12a0ead8a05ac5f4ec3e4814c6b78e3a1b00eff3eea5d5" }, { "codeseparator": null, + "id": "0:149 (bf10c0 #3)", "input": 3, "txfs": "bf10c0", - "txhash": "f56a4095a26dd3578791b2f9bf2e88801c89500f7dc2871137e33317729e8313" + "txhash": "5964ef13cb2407787c2fd686bfff78062312bb5bb1a81cda6421253108911cff" }, { "codeseparator": null, + "id": "0:150 (bf20c0 #0)", "input": 0, "txfs": "bf20c0", - "txhash": "7b5b8cddfb740ab8eee2861ef1a356a2611e23d3d3a4e74c54c8728534cfa393" + "txhash": "8bc4316796e69b25abac010e5ae95378c44b8a522996a2f16369f8a0ed869481" }, { "codeseparator": null, + "id": "0:151 (bf20c0 #1)", "input": 1, "txfs": "bf20c0", - "txhash": "96cc12bc1bc76f73e2cf4510e1126bca528db060372e212d4b9b4acecc1993b9" + "txhash": "36e78973ab9294b4248371950cc064e6debd5501c3b4403e22e4dccf3d0be79d" }, { "codeseparator": null, + "id": "0:152 (bf20c0 #2)", "input": 2, "txfs": "bf20c0", - "txhash": "aca85f5ca541f96e58d286959819e99ab0ba78023e202e40b8fdc8c09d8ec75f" + "txhash": "4224316625430210c4f8b0c20b6648abea7cedf7bbac7eacfa7312d0c471e913" }, { "codeseparator": null, + "id": "0:153 (bf20c0 #3)", "input": 3, "txfs": "bf20c0", - "txhash": "44b1b71bbda59f87662843921acaf333d303b6e44dee61d47635f86224fbd311" + "txhash": "5e122b44de968638acbee647f4bdf47d79e4064cacf944530762a2efb6a0bf65" }, { "codeseparator": null, + "id": "0:154 (bf3fc0 #0)", "input": 0, "txfs": "bf3fc0", - "txhash": "e4ad1c14efa4241f07d6e1856130b7be4684a56cb0d5b294e867f14577fafba8" + "txhash": "3dfec9ecf248ad4dd2bb325e19ae66ff33c7bc63215bfc2bb054cb3e6c70eb3b" }, { "codeseparator": null, + "id": "0:155 (bf3fc0 #1)", "input": 1, "txfs": "bf3fc0", - "txhash": "b70ab3e74903611697ee4acbd83dd38309668e4a55e4b896fd613980d9dcd862" + "txhash": "71231c67a7845d61cc63f2899597b21f010a5f776dff5c2d29c6ef92479dba76" }, { "codeseparator": null, + "id": "0:156 (bf3fc0 #2)", "input": 2, "txfs": "bf3fc0", - "txhash": "e3d82c6c6de142e2cb4da0af6cd4fd0f33ce9a3189ede385f0512b322561cc70" + "txhash": "6888b238b7771c4147d9984dcac5b682c0a0d5011160669acd6b71556c78b003" }, { "codeseparator": null, + "id": "0:157 (bf3fc0 #3)", "input": 3, "txfs": "bf3fc0", - "txhash": "f326187d19bdf2fa2d518170180fec7ceca5972798f6dc315a00a7625e1af332" + "txhash": "abf68afb539a35a1257bc4f7c04bc8d9b2882b0b481da1aa3b1c41b1616c4c10" }, { "codeseparator": null, + "id": "0:158 (bf01bf #0)", "input": 0, "txfs": "bf01bf", - "txhash": "2904ae7e8a1a10c7fbd145fe619252e01d7bd21f1787e3afbf70282effe54133" + "txhash": "13e530a826e4dd2863685285be88363007de486343d111a3f12235a7ff8d71a7" }, { "codeseparator": null, + "id": "0:159 (bf01bf #1)", "input": 1, "txfs": "bf01bf", - "txhash": "ab85f02daf2348c5f0128eda15448c82d3f6830dd1266c3659a5b298a1eac7e4" + "txhash": "9764affaf72185926e2b7611192e98036c520776a4a6181021de622d2de4b04d" }, { "codeseparator": null, + "id": "0:160 (bf01bf #2)", "input": 2, "txfs": "bf01bf", - "txhash": "0ba449340acf50bf5c94a73a902607d93f72b808fc30a3a222173dfa4e22aa25" + "txhash": "ce45e8d563bda897093e5ae8dacd82c36253669f63d1ae203bddfea24e4f9c96" }, { "codeseparator": null, + "id": "0:161 (bf01bf #3)", "input": 3, "txfs": "bf01bf", - "txhash": "cb67d45e1386c92fbc940139093694fca0c3e087465a8e62ddd8a8c0f6269dce" + "txhash": "f9dd819633b0f471e84c4bd97cf266eec393e310f73d37b68f2e904199ce2373" }, { "codeseparator": null, + "id": "0:162 (bf02bf #0)", "input": 0, "txfs": "bf02bf", - "txhash": "2a48b62be39251aa84ce4449cd6105aadbdf4e98f37f248439b7365ce4618e56" + "txhash": "ba05eb61cad20b2ff9664f1b00388fcc17c62b7d90ea2a11775eacd160a37033" }, { "codeseparator": null, + "id": "0:163 (bf02bf #1)", "input": 1, "txfs": "bf02bf", - "txhash": "6918e003a7098e0d3736da752e520c8cb0798ca0e20a991b987963053308e709" + "txhash": "b7d7a181c95271e53970c4ac22f2c7644e0286a94c93dabbff0977611f3b07f6" }, { "codeseparator": null, + "id": "0:164 (bf02bf #2)", "input": 2, "txfs": "bf02bf", - "txhash": "707fa269766ec96221f2a364648483397d076f6cc1945cc555050f9768fb0d53" + "txhash": "2da0277688721236bbbc514027225e19591912c64470468bf676e62980211b3c" }, { "codeseparator": null, + "id": "0:165 (bf02bf #3)", "input": 3, "txfs": "bf02bf", - "txhash": "33001865949e90fbb9bd3fb5765aecb4ced05da24c19f995f4edf66173b0d815" + "txhash": "2edc366bb0eb57feca8f7f3779ce92ed142477809b31a101f3cdbb9b21bb102b" }, { "codeseparator": null, + "id": "0:166 (bf04bf #0)", "input": 0, "txfs": "bf04bf", - "txhash": "3a4be77c748d9b59ac4a83d8d5f548e446863798bb60fb5876a53dbe2dc81454" + "txhash": "f119b71698363633021266fc8870089cb46b38b66b1d0b7516b10841f78ad793" }, { "codeseparator": null, + "id": "0:167 (bf04bf #1)", "input": 1, "txfs": "bf04bf", - "txhash": "e22d699c81521211137a23aab1d94389822c4342494c89b27942e2991ec4c526" + "txhash": "a44b938490c8e50f1cb11bc3f19bdb3cafc80b25461d0418a3dd78337cbaae89" }, { "codeseparator": null, + "id": "0:168 (bf04bf #2)", "input": 2, "txfs": "bf04bf", - "txhash": "b2a3644a82ba4021f9ad5d7507c51dcdd2cbac9ba1d7b6ee80739daed2966b44" + "txhash": "90a1aff219299e352ecf49c9bef2da90285fc8cef88fc5ee31dc642366259e06" }, { "codeseparator": null, + "id": "0:169 (bf04bf #3)", "input": 3, "txfs": "bf04bf", - "txhash": "96fa865973293dae2846a4b4d4a412cea6fa5adb615d97f1ecf7f77f5c6dcd06" + "txhash": "b36106a4096cd747bfa172bd676a17a1fd19af44a2242dd79ae9e1cf6ea41d5e" }, { "codeseparator": null, + "id": "0:170 (bf08bf #0)", "input": 0, "txfs": "bf08bf", - "txhash": "d09224751853e6ec7cef1cc261bab08c5f697cd430e2b7c6435065ce60e15b3d" + "txhash": "071ac3c1db196b98eeaf32935722d063ae37471ab3969643e26aa4d66989b323" }, { "codeseparator": null, + "id": "0:171 (bf08bf #1)", "input": 1, "txfs": "bf08bf", - "txhash": "1a71ef9972fac9ed042d6fa9769084601e62a7db270e9af46aa995ed5488aea0" + "txhash": "93e73ed5055e0441dd9a731e382662aeb709f430ca5ad8094a154af9167f86de" }, { "codeseparator": null, + "id": "0:172 (bf08bf #2)", "input": 2, "txfs": "bf08bf", - "txhash": "58610c8a0a6a0392669489c8769b1c0fc65fa5a09bd10f46f7df71f3fc64a58f" + "txhash": "e4f74ea7bec7ce29a2f1cc53702c43ddc219509d1ad3793dc1ba74fce1f6bbca" }, { "codeseparator": null, + "id": "0:173 (bf08bf #3)", "input": 3, "txfs": "bf08bf", - "txhash": "997795369fa0afddce28fd275c0a94c96ddb3047b11d22d3a436cbe85fb0d89b" + "txhash": "0860ad63d2efef87d6efde20d9080387dd28ad71bb9898db5135c19caff0073a" }, { "codeseparator": null, + "id": "0:174 (bf10bf #0)", "input": 0, "txfs": "bf10bf", - "txhash": "8bec5eff66f7de96ccc037c31885f7dae13796a65b692597d64c5754f7c8cf83" + "txhash": "8ea31d6bb83353a068447bd547b41ee3b1563fd4c83eadfcb707f01f1531b608" }, { "codeseparator": null, + "id": "0:175 (bf10bf #1)", "input": 1, "txfs": "bf10bf", - "txhash": "b7b0090b401715af3291333e09884a97624737b5903f774341acfc68ecc253bf" + "txhash": "a73e7b8cdaa00ccbc3ad2556a822f9384b5839074f3d2126372ad1065335e883" }, { "codeseparator": null, + "id": "0:176 (bf10bf #2)", "input": 2, "txfs": "bf10bf", - "txhash": "5b60a5acdbfe9d41cbe7113b23da7ab05eeb9f1483d4bfb3acea5d68fde27a2e" + "txhash": "cd030648c1326ab4a1f2862fd9af586d3380ad1cb85362b452c1626228a5ff66" }, { "codeseparator": null, + "id": "0:177 (bf10bf #3)", "input": 3, "txfs": "bf10bf", - "txhash": "7b7260a0b97bf6cf51345b436e454038eb2042fb4a821a0606a0f1fc8f639e20" + "txhash": "62db9629232ed494eab57cc2c957f61b900d50db4fb7fd9fd0372f0d8c003b74" }, { "codeseparator": null, + "id": "0:178 (bf20bf #0)", "input": 0, "txfs": "bf20bf", - "txhash": "2334e8398c5b47a357cc223bf8887bbecff1b5d51c3b2b65a5fb8fbfefa5eba2" + "txhash": "5751dc1bc0a8c9bf3ab8bd360836671e963b61ff64c785007d406e1f5fa8450b" }, { "codeseparator": null, + "id": "0:179 (bf20bf #1)", "input": 1, "txfs": "bf20bf", - "txhash": "f172ad19a8463e0184013bd8f1f0cb3f53676c92018821851bff9a3553ef7a9d" + "txhash": "1003eb6423a91c076b72abc6a12ff8b55f5fcc7f764a9937405f6f13cfb9c32c" }, { "codeseparator": null, + "id": "0:180 (bf20bf #2)", "input": 2, "txfs": "bf20bf", - "txhash": "fcf2e4d9809f85c18d9bd7431b1f28c9a720a2445c1f452e4e4b22802a37e1ec" + "txhash": "48fd8d29a4cbbcfe6e485579c9d2f73cef5ae359602e04051d71390239baef00" }, { "codeseparator": null, + "id": "0:181 (bf20bf #3)", "input": 3, "txfs": "bf20bf", - "txhash": "e39b0dd76c8f6de2587fc9f37ff5e20a8b61a643826a26c54b0e11ded5eebc54" + "txhash": "54b67ab8ccfcc7c310019f2f31b5f08ee5dedb6769f8c6db9715144ee19efde3" }, { "codeseparator": null, + "id": "0:182 (bf3fbf #0)", "input": 0, "txfs": "bf3fbf", - "txhash": "704f3966892269f367215ceba260a38f2697b118d43b83bdeb5786e300294397" + "txhash": "a052babf78e9fa0019933e85fcd60fb08c86d2364607904976fc68ef334c494c" }, { "codeseparator": null, + "id": "0:183 (bf3fbf #1)", "input": 1, "txfs": "bf3fbf", - "txhash": "f785c544d541edc20c0196e99055f992399e4576d4b3905e401761cb8585bb7c" + "txhash": "63bbc9a031a1d08d07dca32d62b30d00266010a8016a52477330b5164c6d1ef0" }, { "codeseparator": null, + "id": "0:184 (bf3fbf #2)", "input": 2, "txfs": "bf3fbf", - "txhash": "bcf596c4f29c79cfdfc347c9a800629c15205b341e2eeae59abf9e18f9785e9c" + "txhash": "af79f046431288fc3b9fc066fd6b8a97bc399172138eb13843d760be599b5ebd" }, { "codeseparator": null, + "id": "0:185 (bf3fbf #3)", "input": 3, "txfs": "bf3fbf", - "txhash": "e2e1be3fe1ae57fd217b7b1451f2d2ec203b7a01009fb538bd05240c4bb0f792" + "txhash": "5d71f1d36d9fca323beae94c0960ef6399a4db92ca84a670c83c9a6eadc034a3" }, { "codeseparator": null, + "id": "0:186 (bfff3f3f #0)", "input": 0, - "txfs": "ffff3f3f", - "txhash": "bd9b6f5c22c7da4c380c239a7ea8b132ada659f5a5334a597e9bd41b78ce1c44" + "txfs": "bfff3f3f", + "txhash": "8352120211f422b0291260fbde08f6e3cdf98877ce0da46fa8001b5a00eb9f3b" }, { "codeseparator": null, + "id": "0:187 (bfff3f3f #1)", "input": 1, - "txfs": "ffff3f3f", - "txhash": "7bcb1faec3b3675f667274924f3f8357fe8af7e9dab1c7ab6662e82f249b30c0" + "txfs": "bfff3f3f", + "txhash": "796df7b808c6e0190ebd5a47837b01f5718a5574bfd5518100e10d4ab688b3c7" }, { "codeseparator": null, + "id": "0:188 (bfff3f3f #2)", "input": 2, - "txfs": "ffff3f3f", - "txhash": "426c94659c2cea2f7761103a0780c78c1935ef6eaec5657080b1326c16849600" + "txfs": "bfff3f3f", + "txhash": "9ae4f9caf70ce4fb4e5e52412b9dadb6f481cd9c3022a343a4806da666909afe" }, { "codeseparator": null, + "id": "0:189 (bfff3f3f #3)", "input": 3, - "txfs": "ffff3f3f", - "txhash": "183229534a28400cb72121911896ed76c3fe5b91fdba12d5b8e36ecd2aff2712" + "txfs": "bfff3f3f", + "txhash": "bede7c5efb1c013c519f4e2d304c5d766bdb52ef2b649693a4018bc970378be1" }, { "codeseparator": null, + "id": "0:190 (bfff4040 #0)", "input": 0, - "txfs": "ffff4040", - "txhash": "ecb487752546e951516705c240b5c7d421158b3b1a83ae959d109afd0ce3a208" + "txfs": "bfff4040", + "txhash": "bd3a0060dce0ffee125c7303cf8deb9f53af1cb6f60b388199ed061dae38e026" }, { "codeseparator": null, + "id": "0:191 (bfff4040 #1)", "input": 1, - "txfs": "ffff4040", - "txhash": "78f559915492aff22f62bb24a578825653c473f4ca3ff6d588f2e35de83c676a" + "txfs": "bfff4040", + "txhash": "f9b207b9663e6332b41cd4e690d647f9238336b8cff31716ce62963c3c21a183" }, { "codeseparator": null, + "id": "0:192 (bfff4040 #2)", "input": 2, - "txfs": "ffff4040", - "txhash": "ecab4704755a57b9fc58ad47b6a841e45b9c835721a627b6333d14666b4fe809" + "txfs": "bfff4040", + "txhash": "5486fba88bef133b43a26f68ba953592452d77203a90a8e6a7896341e859726f" }, { "codeseparator": null, + "id": "0:193 (bf008080 #0)", "input": 0, - "txfs": "ffff8080", - "txhash": "0c1059668b37be3cb601854a16a856642cbc22f41f8e4ec3dad84b6540bcf7d6" + "txfs": "bf008080", + "txhash": "5970121aca46194f98e7f7fa955a9500752bdb8cb49a1705f1a946387829d697" }, { "codeseparator": null, + "id": "0:194 (bf008080 #1)", "input": 1, - "txfs": "ffff8080", - "txhash": "23d5cd006d71efea16c0051035a87c90714568c9d780a0477fe9db3e4331652d" + "txfs": "bf008080", + "txhash": "6a5b5fca9796169421e0039227db15cc51279c62db500f1f96a360f6fe176f72" }, { "codeseparator": null, + "id": "0:195 (bf008080 #2)", "input": 2, - "txfs": "ffff8080", - "txhash": "e129745f8a5c962a3989e62fc471c58c734346f09a41e985dcf4417a974b96a6" + "txfs": "bf008080", + "txhash": "c73ea82953932fdffcdca8768eb3f2f42e21c18aefda60ffadb5855ab2b17460" }, { "codeseparator": null, + "id": "0:196 (bf008080 #3)", "input": 3, - "txfs": "ffff8080", - "txhash": "40dd74dc56c6bb8436611325d13e15fa7c0d913bd426252e634463b7e993741f" + "txfs": "bf008080", + "txhash": "425f2c0449b4006e9f293c540e4c978fc9b1d333076a8d52425bad9282c1b2fb" }, { "codeseparator": null, + "id": "0:197 (bfffbfbf #0)", "input": 0, - "txfs": "ffffbfbf", - "txhash": "fe0ec98b3d640ec3bec81e0d9b58a7a563a93beb476c61bf648f6c8b2cb3cc4b" + "txfs": "bfffbfbf", + "txhash": "99139e7afdfcf72202c805d873db042f54f42cb6ddaec47f7c87cbadab0dc6cb" }, { "codeseparator": null, + "id": "0:198 (bfffbfbf #1)", "input": 1, - "txfs": "ffffbfbf", - "txhash": "44004bc5bf81b4bef13f3b1a88019dd7e8557e8dc7384a4cdb4089714b243025" + "txfs": "bfffbfbf", + "txhash": "ad63afd5545942e5bf5b1993e2de70d38d8a84dc311227a55de25180d008a2d1" }, { "codeseparator": null, + "id": "0:199 (bfffbfbf #2)", "input": 2, - "txfs": "ffffbfbf", - "txhash": "5b873279c3719485395261e3161b422252e612cd3a083d0c83136be4da4c1c1b" + "txfs": "bfffbfbf", + "txhash": "af08d21fec7c93cb30096b3f4affd51b386230857d826a63e33304babd708c9b" }, { "codeseparator": null, + "id": "0:200 (bfffbfbf #3)", "input": 3, - "txfs": "ffffbfbf", - "txhash": "244a94e0e8c391b6f105ff2c8da24d26efa67e9b69e1917a17d6ae0c2872b1eb" + "txfs": "bfffbfbf", + "txhash": "934c317da06f8d732a51ddedc9cc3c1d6199b1cb09769df2169d2e9bd666fd32" }, { "codeseparator": null, + "id": "0:201 (bfffc0c0 #0)", "input": 0, - "txfs": "ffffc0c0", - "txhash": "67ab05d39c6b98b0d8b0ffeb892d6435dcbedb3284dcd7ed4912436a3e7ecee0" + "txfs": "bfffc0c0", + "txhash": "175d763318349d6e42d4e5bd66b9171611ebb70f5a742dee86790760add89030" }, { "codeseparator": null, + "id": "0:202 (bfffc0c0 #1)", "input": 1, - "txfs": "ffffc0c0", - "txhash": "667cf47f3d2af2e589135e1fc657bcb2c3008d99c66ae5696f689cdd05987460" + "txfs": "bfffc0c0", + "txhash": "d6420eeed76078e246d829fc72d6a3ca31a535a92ae6765e2fa36b948355ce7a" }, { "codeseparator": null, + "id": "0:203 (bfffc0c0 #2)", "input": 2, - "txfs": "ffffc0c0", - "txhash": "0034079e8ee835e01539a90ed12960df0c20d00ce768e5f8cff81f4b1de56718" + "txfs": "bfffc0c0", + "txhash": "218c08631daf9e33d3c3c2e66bd9c62b5d415a0c36ea784306c8d6df34bc4f4a" }, { "codeseparator": null, + "id": "0:204 (bfff403f #0)", "input": 0, - "txfs": "ffff403f", - "txhash": "de613bc1538f52435e2fcb520d98f5cbfb9922ba6b241c46964ac5a6058bf26d" + "txfs": "bfff403f", + "txhash": "e28e7988a2d4c722993a172853cf190d9ad5379c77ce5a6739fc283712d0dd9f" }, { "codeseparator": null, + "id": "0:205 (bfff403f #1)", "input": 1, - "txfs": "ffff403f", - "txhash": "ff2e38e426425ac5913674afde5e8113dcacdb32984e93aa31ced26390f2f45c" + "txfs": "bfff403f", + "txhash": "eae430940a74ec50e5036214fa475b897de9a098383d944ebbd7f57b7a8d6ff8" }, { "codeseparator": null, + "id": "0:206 (bfff403f #2)", "input": 2, - "txfs": "ffff403f", - "txhash": "cefb9122e27b6faf022b8d5ce8e3ae592b320edd176c4c5394b86ad894553dc4" + "txfs": "bfff403f", + "txhash": "f2f7ab612fff345e0258e9233cc8f23c1e9cc354833f50245beb603db77bc7f4" }, { "codeseparator": null, + "id": "0:207 (bfff403f #3)", "input": 3, - "txfs": "ffff403f", - "txhash": "34535aab9e71af1fe7db821e7e8c5895b2a30b06cf5a03b5974a5d0bebabaea4" + "txfs": "bfff403f", + "txhash": "c520cccc4ad5977ff47647f457fecf7be0a019ae789860131619129d59d25e43" }, { "codeseparator": null, + "id": "0:208 (bfff3f40 #0)", "input": 0, - "txfs": "ffff3f40", - "txhash": "c593165638d0d7d157c3bdc5295464d6f201c3de660971668c24acb4ff694e1a" + "txfs": "bfff3f40", + "txhash": "2f8a3083a15694cfebcf1dcfe73670466c3df14463f5ed956326f49a93e13903" }, { "codeseparator": null, + "id": "0:209 (bfff3f40 #1)", "input": 1, - "txfs": "ffff3f40", - "txhash": "a91f5fe5a174ce47d5d86c54432b10dc1110db85bc4d6a98243306cb997deb21" + "txfs": "bfff3f40", + "txhash": "5c500fc6ab63299fbaa489b9318eaade34ea59a3b89e6b5c1260e8c32e29f2d3" }, { "codeseparator": null, + "id": "0:210 (bfff3f40 #2)", "input": 2, - "txfs": "ffff3f40", - "txhash": "6e305601b18038736da36ec3b2ac4c8547e5e31c2666ac6e52665435b0cb90a2" + "txfs": "bfff3f40", + "txhash": "59ef73e6a3e19a859790a944104b28a3caad931befc54a9a6f6c642d9eedfc4f" }, { "codeseparator": null, + "id": "0:211 (bfff0182 #0)", "input": 0, - "txfs": "ffff0102", - "txhash": "1126186623a5da2b205ce726176fff52cd8ea13d1700fbec2430c6b21fb1947b" + "txfs": "bfff0182", + "txhash": "fcedfac535843c32427207c9db69d51d6f00abc40d70dba7742a91067a2d8189" }, { "codeseparator": null, + "id": "0:212 (bfff0182 #1)", "input": 1, - "txfs": "ffff0102", - "txhash": "61ae007ce70ce68b324a9f87091e1b9fa3108e93aebb44cef92206ee1610f3d9" + "txfs": "bfff0182", + "txhash": "e5eba524dded1b075815c1f93a916fc060c9b066884eb61d02abd699b8aca73b" }, { "codeseparator": null, + "id": "0:213 (bfff0182 #2)", "input": 2, - "txfs": "ffff0102", - "txhash": "88453084e176f7ea39815b1d971149d1e9eedee019d7073143ee7581f83773aa" + "txfs": "bfff0182", + "txhash": "aac43bc494d5f488b5272d818170e011d7a5e0c6f2c3e593b2bf3b0b51787a11" }, { "codeseparator": null, + "id": "0:214 (bfff0182 #3)", "input": 3, - "txfs": "ffff0102", - "txhash": "30a1e19ef5b0787f1d73fe5f20b849e20861fd950833f300f09018e53d9ded0b" + "txfs": "bfff0182", + "txhash": "1c6306da9a8864b4be2323e4a9e12c00eb27c3a6c40e8a22506fd433e5e9f8fb" }, { "codeseparator": null, + "id": "0:215 (bfffc002 #0)", "input": 0, - "txfs": "ffff4101420002", - "txhash": "f716067fe48fafd0dc2b8eb7c66bc1e6c5728267679f54c8c6701e512865dc7e" + "txfs": "bfffc002", + "txhash": "9d0fcba07903ab0b5d514b0d4504dd3644e1a04a1fa0926310b673bcb0bac625" }, { "codeseparator": null, + "id": "0:216 (bfffc002 #1)", "input": 1, - "txfs": "ffff4101420002", - "txhash": "b33949998e8593e81a062494955b43b0efc7d6536f11e3128fb68a1c26e432d5" + "txfs": "bfffc002", + "txhash": "c325c9a8f2b875f59b7c15a6441f1b3910682a2783725dafd74ce6ef1142bbc0" }, { "codeseparator": null, + "id": "0:217 (bfffc002 #2)", "input": 2, - "txfs": "ffff4101420002", - "txhash": "91f2e9acae60c7550361da5aebd121794f379684541ca3fe0e14b682f2aa9e3d" + "txfs": "bfffc002", + "txhash": "74ae80b6f986d09462494f86f0892b41db673ecc4f18bd97ab93736f4f1ea197" }, { "codeseparator": null, + "id": "0:218 (bfff4101420002 #0)", + "input": 0, + "txfs": "bfff4101420002", + "txhash": "c272e0e2eabbf7d7fcc02d4bb3c91293051a1545be0c30312b66117471f6fb35" + }, + { + "codeseparator": null, + "id": "0:219 (bfff4101420002 #1)", + "input": 1, + "txfs": "bfff4101420002", + "txhash": "f37687e23ff5fbc3338b7918bac938c82cf9506b37f6fa2e2d83aaf93524b5df" + }, + { + "codeseparator": null, + "id": "0:220 (bfff4101420002 #2)", + "input": 2, + "txfs": "bfff4101420002", + "txhash": "fbdc9f142020acb625088bb67ed590af11a05a7607586b68644171dc66219b29" + }, + { + "codeseparator": null, + "id": "0:221 (bfff4101420002 #3)", "input": 3, - "txfs": "ffff4101420002", - "txhash": "5e60916b38ecc08902ff6edb07a0ca59c788a5b94f2b6cb45dd3473c0c5c6676" + "txfs": "bfff4101420002", + "txhash": "e58121a9d4e5ae4f522c7eb39df94dff04dae5573ffcd0b810117589ec7febcc" }, { "codeseparator": null, + "id": "0:222 (bfffc101c20002 #0)", "input": 0, - "txfs": "ffffc101c20002", - "txhash": "3985e4a2cb45548f4f65c1bc19ce4a39b1f377b9846bf9d0b638756511e46519" + "txfs": "bfffc101c20002", + "txhash": "73b3fe9fa2f90f063966aadca0a36a9093725ad040af6b070135d1503b620d48" }, { "codeseparator": null, + "id": "0:223 (bfffc101c20002 #1)", "input": 1, - "txfs": "ffffc101c20002", - "txhash": "15b181822828e7dfa1cae09e9e461655f87fc03de4b6c4b338966a397665acad" + "txfs": "bfffc101c20002", + "txhash": "46aa5607904cfadd3483b7a61706f8b0d7089d19b4d6fed02f071bedc7b6dbae" }, { "codeseparator": null, + "id": "0:224 (bfffc101c20002 #2)", "input": 2, - "txfs": "ffffc101c20002", - "txhash": "56e33218cda9d407a8e05939100c90ff32f692b4ab5902dda2852a6be96576da" + "txfs": "bfffc101c20002", + "txhash": "f588b0e1a97e51b0cca91258159262e5a08758a165d2d96d3b1f46f13e653b5f" }, { "codeseparator": null, + "id": "0:225 (bfffc101c20002 #3)", "input": 3, - "txfs": "ffffc101c20002", - "txhash": "39e4475f09fbe045174ad7f1bd0db8da8158a542f53380e1e26c06cd098068ea" + "txfs": "bfffc101c20002", + "txhash": "fffdc8629c2ac13efcf66d1cb79c5b8e6bfcdefb4e541740682fee048d7de7a1" + }, + { + "codeseparator": null, + "id": "0:226 (bfff617f627f00 #1)", + "input": 1, + "txfs": "bfff617f627f00", + "txhash": "3abc580aa956e40cb60aef3eb07135e910eff2f4c00211876a0863e8fa79d89c" + }, + { + "codeseparator": null, + "id": "0:227 (bfff617f627f00 #2)", + "input": 2, + "txfs": "bfff617f627f00", + "txhash": "d9503d415d9a94fa88fe4139a3afa6f3b77f28b693fba9df31945ef6f37548b5" + }, + { + "codeseparator": null, + "id": "0:228 (bfffe17fe27f00 #1)", + "input": 1, + "txfs": "bfffe17fe27f00", + "txhash": "20f12c5960847e0a7d1ee98043787a3e937cb830bfe0aedf233b5456964535ec" + }, + { + "codeseparator": null, + "id": "0:229 (bfffe17fe27f00 #2)", + "input": 2, + "txfs": "bfffe17fe27f00", + "txhash": "fb1a1e2760d3b93f33e92be7080d8ba676dcae379e0e541a3fc59269f5ed9d48" }, { "codeseparator": null, + "id": "0:230 ( #0)", "input": 0, "txfs": "", - "txhash": "723c1c2f5947763d0a3f0d545e5b1d041eb7195037161b65a568ee5977ac486f" + "txhash": "f4afe580e3bcf635c46c12f224f259f9354a4eff06e81550e35aa553820ba88f" }, { "codeseparator": null, + "id": "0:231 ( #1)", "input": 1, "txfs": "", - "txhash": "dbb5b4c37596ccfc3ea19931bed2d7f67ccd2362f95cd34e34504b50df724a71" + "txhash": "8f1becde41b585ab91b8039c9ab95091f18b4f52d5aa906ae5c22b62c623f07a" }, { "codeseparator": null, + "id": "0:232 ( #2)", "input": 2, "txfs": "", - "txhash": "4cc217e7a101d94c5b54a0442a591933239fb112059ed229d594b8fcee76f3c3" + "txhash": "9f1a08a5dc364939ac23ee245bf4c258d2cfe88a6c8305aa4b834ce57faf671c" }, { "codeseparator": null, + "id": "0:233 ( #3)", "input": 3, "txfs": "", - "txhash": "c64c106fb50f3ba80cf6887cc7161933fc33c658baba57156a7d66b6cd10d35e" + "txhash": "cde83ca2e79ac4277477995f48b2e63ae41b35e1ebf07b39bfe9ce0f994dd79f" }, { "codeseparator": null, + "id": "0:234 (00 #0)", "input": 0, "txfs": "00", - "txhash": "fe0ec98b3d640ec3bec81e0d9b58a7a563a93beb476c61bf648f6c8b2cb3cc4b" + "txhash": "99139e7afdfcf72202c805d873db042f54f42cb6ddaec47f7c87cbadab0dc6cb" }, { "codeseparator": null, + "id": "0:235 (00 #1)", "input": 1, "txfs": "00", - "txhash": "44004bc5bf81b4bef13f3b1a88019dd7e8557e8dc7384a4cdb4089714b243025" + "txhash": "ad63afd5545942e5bf5b1993e2de70d38d8a84dc311227a55de25180d008a2d1" }, { "codeseparator": null, + "id": "0:236 (00 #2)", "input": 2, "txfs": "00", - "txhash": "5b873279c3719485395261e3161b422252e612cd3a083d0c83136be4da4c1c1b" + "txhash": "af08d21fec7c93cb30096b3f4affd51b386230857d826a63e33304babd708c9b" }, { "codeseparator": null, + "id": "0:237 (00 #3)", "input": 3, "txfs": "00", - "txhash": "244a94e0e8c391b6f105ff2c8da24d26efa67e9b69e1917a17d6ae0c2872b1eb" + "txhash": "934c317da06f8d732a51ddedc9cc3c1d6199b1cb09769df2169d2e9bd666fd32" } ] } From 866ff8c402df46a96dd5697ae37049b8ba514963 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Mon, 29 Apr 2024 18:15:12 +0100 Subject: [PATCH 3/4] txhash: Bump validation weight cost from 15 to 25 --- bip-txhash.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bip-txhash.md b/bip-txhash.md index 45d8a41384..5445de3270 100644 --- a/bip-txhash.md +++ b/bip-txhash.md @@ -280,7 +280,7 @@ bf ff c2 01 03 83 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 15. If this brings the budget below zero, + 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. From 2258ebe48cd387ff2f05e1881f54640815b4ab07 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Mon, 29 Apr 2024 22:35:17 +0100 Subject: [PATCH 4/4] txhash: Remove the malleability checks Instead, add a notice about malleability. --- bip-txhash.md | 21 ++++++----------- bip-txhash/ref-impl/src/main.rs | 40 +++------------------------------ 2 files changed, 10 insertions(+), 51 deletions(-) diff --git a/bip-txhash.md b/bip-txhash.md index 5445de3270..5bb2e4760e 100644 --- a/bip-txhash.md +++ b/bip-txhash.md @@ -172,20 +172,13 @@ Effectively, this allows a user to select ** using absolute indices up to 16384 ** using indices relative to the current input index from -8191 to +8192. -The TxFieldSelector is invalid when -* a byte is expected but missing -* additional unexpected bytes are present -* index size is set to 1 while not being necessary -* a leading number or individual index is selected out of bounds of the in/outputs -* individual indices are duplicated or not in increasing order -* single relative index of +0, which could be just `TXFS_INOUT_SELECTION_CURRENT` -* input or output fields bits are set, but no inputs or outputs are selected - -These limitations are to avoid potential TxFieldSelector malleability. It is -however allowed to use leading mode where it could be "all". This -is important to allow for optional addition of extra inputs or outputs. - -//TODO(stevenroose) should we disallow individual that could be leading? +### 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 diff --git a/bip-txhash/ref-impl/src/main.rs b/bip-txhash/ref-impl/src/main.rs index c451478a34..8f41d0ce38 100644 --- a/bip-txhash/ref-impl/src/main.rs +++ b/bip-txhash/ref-impl/src/main.rs @@ -102,9 +102,6 @@ fn parse_inout_selection( let selection = first & (0xff ^ TXFS_INOUT_NUMBER); let selected = if selection == TXFS_INOUT_SELECTION_NONE { - if !allow_empty && !commit_number { - return Err("no selection made and also not commiting count"); - } vec![] } else if selection == TXFS_INOUT_SELECTION_ALL { (0..nb_items).collect() @@ -118,9 +115,6 @@ fn parse_inout_selection( let count = if (selection & TXFS_INOUT_LEADING_SIZE) == 0 { (selection & TXFS_INOUT_SELECTION_MASK) as usize } else { - if (selection & TXFS_INOUT_SELECTION_MASK) == 0 { - return Err("non-minimal leading selection"); - } let next_byte = bytes.next().ok_or("second leading selection byte missing")?; (((selection & TXFS_INOUT_SELECTION_MASK) as usize) << 8) + next_byte as usize }; @@ -133,9 +127,6 @@ fn parse_inout_selection( let absolute = (selection & TXFS_INOUT_INDIVIDUAL_MODE) == 0; let count = (selection & TXFS_INOUT_SELECTION_MASK) as usize; - if count == 0 { - return Err("can't select 0 in/outputs in individual mode"); - } let mut selected = Vec::with_capacity(count as usize); for _ in 0..count { @@ -144,9 +135,6 @@ fn parse_inout_selection( let number = if single_byte { first as usize } else { - if first == 0 { - return Err("unnecessary two-byte index"); - } let next_byte = bytes.next().ok_or("expected another index byte")?; (((first & (1 << 7)) as usize) << 8) + next_byte as usize }; @@ -160,9 +148,6 @@ fn parse_inout_selection( read_i15(number as u16) as isize }; - if rel == 0 && count == 1 { - return Err("not allowed to only have a single relative index of 0"); - } if rel.is_negative() && rel.abs() > current_input_idx as isize { return Err("relative index out of bounds"); } @@ -259,22 +244,11 @@ pub fn calculate_txhash( None => return Ok(sha256::Hash::from_engine(engine)), }; - if bytes.peek().is_none() { - return Err("in/out field byte set, but no input/outputs selected"); - } - // Inputs let (input_select, commit_number_inputs) = parse_inout_selection( &mut bytes, tx.input.len(), current_input_idx, true, )?; - if (inout_fields & TXFS_INPUTS_ALL) == 0 && !input_select.is_empty() { - return Err("input selection given but no input field bits set"); - } - if (inout_fields & TXFS_INPUTS_ALL) != 0 && input_select.is_empty() { - return Err("no input selection given but some input field bits set"); - } - if commit_number_inputs { (tx.input.len() as u32).consensus_encode(&mut engine).unwrap(); } @@ -355,22 +329,12 @@ pub fn calculate_txhash( // Outputs if bytes.peek().is_none() { - if (inout_fields & TXFS_OUTPUTS_ALL) != 0 { - return Err("some output field bits set, but no outputs selected"); - } } 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 (inout_fields & TXFS_OUTPUTS_ALL) == 0 && !selection.is_empty() { - return Err("no output field bits set, but output selection provided"); - } - if (inout_fields & TXFS_OUTPUTS_ALL) != 0 && selection.is_empty() { - return Err("output field bits set, but no output selection provided"); - } - if commit_number { (tx.output.len() as u32).consensus_encode(&mut engine).unwrap(); } @@ -398,7 +362,9 @@ pub fn calculate_txhash( } } - assert!(bytes.next().is_none(), "unused txfs bytes"); + if bytes.next().is_some() { + return Err("unused txfs bytes"); + } Ok(sha256::Hash::from_engine(engine)) }