From 71073ff9a6873a73cfd752ef3de3ee9ff5e916f1 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Wed, 6 Sep 2023 16:16:35 +0100 Subject: [PATCH] Add BIP for OP_TXHASH and OP_CHECKTXHASHVERIFY --- bip-txhash.mediawiki | 230 ++++ 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, 2430 insertions(+) create mode 100644 bip-txhash.mediawiki 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.mediawiki b/bip-txhash.mediawiki new file mode 100644 index 0000000000..c81cd9016b --- /dev/null +++ b/bip-txhash.mediawiki @@ -0,0 +1,230 @@ +
+  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 uses opcode OP_NOP4 (0xb3) as a soft fork upgrade. + +OP_CHECKTXHASHVERIFY does the following: + +* 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 uses tapscript opcode OP_SUCCESS189 (0xbd) as a soft fork upgrade. + +OP_TXHASH does the following: + +* 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. + + +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 0xff|0xf6|0xbf|0xbf, + the de-facto default value which means everything except the prevouts and + the prevout scriptPubkeys. + - the 0x00 byte: it is set equal to 0xff|0xff|0xbf|0xbf, which means "ALL" + and is primarily useful to emulate SIGHASH_ALL when OP_TXHASH is used in + combination with OP_CHECKSIGFROMSTACK. + +* The first byte of the TxFieldSelector has its 8 bits assigned as follows, + from lowest to highest: + 1. version + 2. locktime + 3. current input index + 4. current input control block (or empty) + 5. current script last OP_CODESEPARATOR position (or 0xffffffff) + 6. inputs + 7. outputs + +* The last (highest) bit of the first byte, 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 + 2. sequences + 3. scriptSigs + 4. prevout scriptPubkeys + 5. prevout values + 6. taproot annexes + * Specifying which fields of the outputs will be selected: + 7. scriptPubkeys + 8. 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 indicates whether the "number of in-/outputs" should be + committed to. + * For the remaining bits, there are three exceptional values: + - 0x00 means "no in/outputs" (hence only the number of them as 0x80). + - 0x40 means "select only the in/output of the current input index" + (it is invalid when current index exceeds number of outputs). + - 0x3f means "select all in/outputs". + * The second highest bit is the "specification mode": + - Set to 0 it means "leading mode". + - Set to 1 it means "individual mode". + * The third highest bit is used to indicate the "index size", i.e. the number + of bytes will be used to represent in/output indices. + * In "leading mode", + - 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 5 bits of the first byte will be + interpreted as `n`, the number of individual in/outputs to select. + - With "index size" set to 0, interpret the following `n` individual bytes + as the indices of an individual in/outputs to select. + - With "index size" set to 1, interpret the next `n` pairs of two bytes as + the indices of individual in/outputs to select. + +Effectively, this allows a user to select +- all in/outputs +- the current input index +- the leading in/outputs up to 8192 +- up to 32 individually selected in/outputs + +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 "prefix 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 use of 64-bit arithmetic. + +* 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. + + +==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 CTV 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..647d7ca927 --- /dev/null +++ b/bip-txhash/ref-impl/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "txhash-ref" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitcoin = { version = "0.31.0", features = [ "serde" ] } +serde_json = "1.0.108" + +[patch.crates-io] +bitcoin = { path = "/home/steven/code/rust/bitcoin/bitcoin" } +bitcoin-io = { path = "/home/steven/code/rust/bitcoin/io" } diff --git a/bip-txhash/ref-impl/src/main.rs b/bip-txhash/ref-impl/src/main.rs new file mode 100644 index 0000000000..7f2ff8572c --- /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_SCRIPT_PUBKEYS: 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_DEFAULT: u8 = TXFS_INPUTS_SEQUENCES + | TXFS_INPUTS_SCRIPTSIGS + | TXFS_INPUTS_PREV_VALUES + | TXFS_INPUTS_TAPROOT_ANNEXES; +pub const TXFS_OUTPUTS_ALL: u8 = TXFS_OUTPUTS_SCRIPT_PUBKEYS | 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_TEMPLATE_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_TEMPLATE_DEFAULT: [u8; 4] = [ + TXFS_ALL, + TXFS_INPUTS_DEFAULT | 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_TEMPLATE_DEFAULT + } else if txfs.len() == 1 && txfs[0] == 0x00 { + &TXFS_TEMPLATE_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_SCRIPT_PUBKEYS != 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_SCRIPT_PUBKEYS, TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_SELECTION_CURRENT], + &[0xdf, TXFS_OUTPUTS_SCRIPT_PUBKEYS, TXFS_INOUT_SELECTION_ALL], + &[0xdf, TXFS_OUTPUTS_VALUES, TXFS_INOUT_SELECTION_ALL], + &[0xdf, TXFS_OUTPUTS_ALL, TXFS_INOUT_SELECTION_ALL], + &[0xdf, TXFS_OUTPUTS_SCRIPT_PUBKEYS, 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_SCRIPT_PUBKEYS, 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_SCRIPT_PUBKEYS, 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