From 86a181b821c62806275e5d33d357ecd3dd11918e Mon Sep 17 00:00:00 2001 From: Miranda Wood Date: Fri, 22 Mar 2024 19:08:08 +0000 Subject: [PATCH] feat: remove NUM_FIELDS_PER_SHA256 (#5392) Continuation of #5160 This PR removes all reference to NUM_FIELDS_PER_SHA256, as we are truncating SHAs to 31 bytes inside the circuits/contracts, so they can be represented as a single field. It also tidies up `toTruncField(sha256(thing))` using a new TS method `sha256ToField`. `toTruncField` now never actually truncates the number, as it expects a truncated output from `sha256ToField` or directly from Noir, which should solve any issues with tests where test values weren't getting truncated correctly. --------- Co-authored-by: sklppy88 Co-authored-by: esau <152162806+sklppy88@users.noreply.github.com> --- .../src/core/libraries/ConstantsGen.sol | 1 - .../messagebridge/frontier_tree/Frontier.sol | 2 +- .../aztec/src/context/private_context.nr | 9 +- .../aztec/src/context/public_context.nr | 11 +- .../aztec-nr/aztec/src/oracle/logs.nr | 10 +- .../src/utils/sha256_merkle_tree.nr | 4 +- .../src/private_kernel_init.nr | 8 +- .../src/private_kernel_inner.nr | 8 +- .../crates/public-kernel-lib/src/common.nr | 8 +- .../src/public_kernel_app_logic.nr | 6 +- .../src/public_kernel_setup.nr | 6 +- .../src/public_kernel_teardown.nr | 6 +- .../base_or_merge_rollup_public_inputs.nr | 6 +- .../rollup-lib/src/base/base_rollup_inputs.nr | 23 +- .../crates/rollup-lib/src/components.nr | 40 +- .../crates/rollup-lib/src/root.nr | 6 +- .../rollup-lib/src/root/root_rollup_inputs.nr | 2 +- .../src/tests/previous_rollup_data.nr | 8 +- .../accumulated_revertible_data_builder.nr | 8 +- .../combined_accumulated_data.nr | 8 +- .../combined_accumulated_data_builder.nr | 8 +- .../private_accumulated_revertible_data.nr | 8 +- .../public_accumulated_revertible_data.nr | 8 +- .../src/abis/private_circuit_public_inputs.nr | 15 +- .../src/abis/public_circuit_public_inputs.nr | 10 +- .../crates/types/src/constants.nr | 2 - .../crates/types/src/content_commitment.nr | 33 +- .../crates/types/src/hash.nr | 17 +- .../types/src/tests/kernel_data_builder.nr | 6 +- .../src/tests/private_call_data_builder.nr | 6 +- .../private_circuit_public_inputs_builder.nr | 8 +- .../src/tests/public_call_data_builder.nr | 4 +- .../public_circuit_public_inputs_builder.nr | 5 +- yarn-project/circuit-types/src/l2_block.ts | 6 +- .../src/messaging/l1_to_l2_message.ts | 6 +- .../circuit-types/src/mocks_to_purge.ts | 5 +- .../circuit-types/src/tx/processed_tx.ts | 6 +- yarn-project/circuits.js/src/constants.gen.ts | 1 - .../__snapshots__/contract_class.test.ts.snap | 10 +- .../src/structs/content_commitment.ts | 29 +- .../kernel/combined_accumulated_data.ts | 57 ++- .../structs/private_circuit_public_inputs.ts | 25 +- .../structs/public_circuit_public_inputs.ts | 13 +- .../base_or_merge_rollup_public_inputs.ts | 20 +- .../circuits.js/src/tests/factories.ts | 23 +- .../src/e2e_cross_chain_messaging.test.ts | 32 +- .../end-to-end/src/e2e_outbox.test.ts | 24 +- .../e2e_public_cross_chain_messaging.test.ts | 52 +-- .../src/integration_l1_publisher.test.ts | 5 +- .../src/shared/cross_chain_test_harness.ts | 41 +- .../end-to-end/src/shared/uniswap_l1_l2.ts | 407 ++++++++---------- .../foundation/src/crypto/sha256/index.ts | 7 + .../src/serialize/free_funcs.test.ts | 2 +- .../foundation/src/serialize/free_funcs.ts | 8 +- .../src/__snapshots__/index.test.ts.snap | 16 +- .../src/type_conversion.ts | 52 ++- .../src/orchestrator/orchestrator.test.ts | 5 +- .../src/sequencer/abstract_phase_manager.ts | 4 +- .../src/sequencer/public_processor.test.ts | 14 +- .../simulator/src/client/private_execution.ts | 5 +- yarn-project/simulator/src/test/utils.ts | 5 +- 61 files changed, 553 insertions(+), 637 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index ae79f767956..aac178b843c 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -72,7 +72,6 @@ library Constants { uint256 internal constant L1_TO_L2_MSG_SUBTREE_HEIGHT = 4; uint256 internal constant L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = 12; uint256 internal constant FUNCTION_SELECTOR_NUM_BYTES = 4; - uint256 internal constant NUM_FIELDS_PER_SHA256 = 1; uint256 internal constant ARGS_HASH_CHUNK_LENGTH = 32; uint256 internal constant ARGS_HASH_CHUNK_COUNT = 32; uint256 internal constant INITIALIZATION_SLOT_SEPARATOR = 1000_000_000; diff --git a/l1-contracts/src/core/messagebridge/frontier_tree/Frontier.sol b/l1-contracts/src/core/messagebridge/frontier_tree/Frontier.sol index cb2ee1f36af..650dfbd30a1 100644 --- a/l1-contracts/src/core/messagebridge/frontier_tree/Frontier.sol +++ b/l1-contracts/src/core/messagebridge/frontier_tree/Frontier.sol @@ -36,7 +36,7 @@ contract FrontierMerkle is IFrontier { uint256 level = _computeLevel(index); bytes32 right = _leaf; for (uint256 i = 0; i < level; i++) { - right = Hash.sha256ToField(bytes.concat(frontier[i], bytes32(right))); + right = Hash.sha256ToField(bytes.concat(frontier[i], right)); } frontier[level] = right; diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 4b70bfd7b6b..d8e32b567cd 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -24,8 +24,7 @@ use dep::protocol_types::{ MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, - NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH + MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, RETURN_VALUES_LENGTH }, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, grumpkin_private_key::GrumpkinPrivateKey, hash::hash_args, header::Header, @@ -156,8 +155,8 @@ impl PrivateContext { pub fn finish(self) -> PrivateCircuitPublicInputs { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - let encrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; - let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; + let encrypted_logs_hash = 0; + let unencrypted_logs_hash = 0; let encrypted_log_preimages_length = 0; let unencrypted_log_preimages_length = 0; @@ -471,7 +470,7 @@ impl PrivateContext { new_l2_to_l1_msgs: [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL], start_side_effect_counter: 0, end_side_effect_counter: 0, - unencrypted_logs_hash: [0; NUM_FIELDS_PER_SHA256], + unencrypted_logs_hash: 0, unencrypted_log_preimages_length: 0, historical_header: Header::empty(), prover_address: AztecAddress::zero(), diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index a8d85a67c85..42a9ed900f6 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -15,8 +15,7 @@ use dep::protocol_types::{ MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, - NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH + MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, RETURN_VALUES_LENGTH }, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, hash::hash_args, header::Header, messaging::l2_to_l1_message::L2ToL1Message, utils::reader::Reader @@ -39,8 +38,8 @@ struct PublicContext { new_nullifiers: BoundedVec, new_l2_to_l1_msgs: BoundedVec, - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - unencrypted_logs_hash: BoundedVec, + + unencrypted_logs_hash: Field, unencrypted_logs_preimages_length: Field, // Header of a block whose state is used during public execution. Set by sequencer to be a header of a block @@ -64,7 +63,7 @@ impl PublicContext { new_note_hashes: BoundedVec::new(), new_nullifiers: BoundedVec::new(), new_l2_to_l1_msgs: BoundedVec::new(), - unencrypted_logs_hash: BoundedVec::new(), + unencrypted_logs_hash: 0, unencrypted_logs_preimages_length: 0, historical_header: inputs.historical_header, prover_address: AztecAddress::zero() // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) @@ -121,7 +120,7 @@ impl PublicContext { pub fn finish(self) -> PublicCircuitPublicInputs { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256]; + let unencrypted_logs_hash = 0; let unencrypted_log_preimages_length = 0; // Compute the public call stack hashes diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr index 01751c1d3b6..7335cbec6ea 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr @@ -1,4 +1,4 @@ -use dep::protocol_types::{address::AztecAddress, constants::NUM_FIELDS_PER_SHA256, grumpkin_point::GrumpkinPoint}; +use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; // TODO: Should take encrypted data. #[oracle(emitEncryptedLog)] @@ -16,8 +16,7 @@ unconstrained pub fn emit_encrypted_log( note_type_id: Field, encryption_pub_key: GrumpkinPoint, preimage: [Field; N] -) -> [Field; NUM_FIELDS_PER_SHA256] { - [ +) -> Field { emit_encrypted_log_oracle( contract_address, storage_slot, @@ -25,7 +24,6 @@ unconstrained pub fn emit_encrypted_log( encryption_pub_key, preimage ) - ] } #[oracle(emitUnencryptedLog)] @@ -39,7 +37,7 @@ unconstrained pub fn emit_unencrypted_log( contract_address: AztecAddress, event_selector: Field, message: T -) -> [Field; NUM_FIELDS_PER_SHA256] { +) -> Field { // https://github.com/AztecProtocol/aztec-packages/issues/885 - [emit_unencrypted_log_oracle(contract_address, event_selector, message)] + emit_unencrypted_log_oracle(contract_address, event_selector, message) } diff --git a/noir-projects/noir-protocol-circuits/crates/parity-lib/src/utils/sha256_merkle_tree.nr b/noir-projects/noir-protocol-circuits/crates/parity-lib/src/utils/sha256_merkle_tree.nr index 14a60b60dbb..0e969cd3fb3 100644 --- a/noir-projects/noir-protocol-circuits/crates/parity-lib/src/utils/sha256_merkle_tree.nr +++ b/noir-projects/noir-protocol-circuits/crates/parity-lib/src/utils/sha256_merkle_tree.nr @@ -26,7 +26,7 @@ impl Sha256MerkleTree { leaves[2*i], leaves[2*i+1] ] - )[0]; + ); } // hash the other layers @@ -36,7 +36,7 @@ impl Sha256MerkleTree { nodes[2*i], nodes[2*i+1] ] - )[0]; + ); } Sha256MerkleTree { leaves, nodes } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr index 9abe7db0de5..73139ca59b2 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr @@ -158,9 +158,9 @@ mod tests { let mut builder = PrivateKernelInitInputsBuilder::new(); // Logs for the private call. - let encrypted_logs_hash = [16]; + let encrypted_logs_hash = 16; let encrypted_log_preimages_length = 100; - let unencrypted_logs_hash = [26]; + let unencrypted_logs_hash = 26; let unencrypted_log_preimages_length = 50; builder.private_call.set_encrypted_logs(encrypted_logs_hash, encrypted_log_preimages_length); builder.private_call.set_unencrypted_logs(unencrypted_logs_hash, unencrypted_log_preimages_length); @@ -176,10 +176,10 @@ mod tests { assert_eq(public_inputs.end.unencrypted_log_preimages_length, unencrypted_log_preimages_length); // Logs hashes should be a sha256 hash of a 0 value (the previous log hash) and the `(un)encrypted_logs_hash` from private input - let expected_encrypted_logs_hash = compute_logs_hash([0], encrypted_logs_hash); + let expected_encrypted_logs_hash = compute_logs_hash(0, encrypted_logs_hash); assert_eq(public_inputs.end.encrypted_logs_hash, expected_encrypted_logs_hash); - let expected_unencrypted_logs_hash = compute_logs_hash([0], unencrypted_logs_hash); + let expected_unencrypted_logs_hash = compute_logs_hash(0, unencrypted_logs_hash); assert_eq(public_inputs.end.unencrypted_logs_hash, expected_unencrypted_logs_hash); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr index 5dcf4422fea..3b03d951d47 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -673,17 +673,17 @@ mod tests { let mut builder = PrivateKernelInnerInputsBuilder::new(); // Logs for the current call stack. - let encrypted_logs_hash = [16]; + let encrypted_logs_hash = 16; let encrypted_log_preimages_length = 100; - let unencrypted_logs_hash = [26]; + let unencrypted_logs_hash = 26; let unencrypted_log_preimages_length = 50; builder.private_call.set_encrypted_logs(encrypted_logs_hash, encrypted_log_preimages_length); builder.private_call.set_unencrypted_logs(unencrypted_logs_hash, unencrypted_log_preimages_length); // Logs for the previous call stack. - let prev_encrypted_logs_hash = [80]; + let prev_encrypted_logs_hash = 80; let prev_encrypted_log_preimages_length = 13; - let prev_unencrypted_logs_hash = [956]; + let prev_unencrypted_logs_hash = 956; let prev_unencrypted_log_preimages_length = 24; builder.previous_kernel.set_encrypted_logs(prev_encrypted_logs_hash, prev_encrypted_log_preimages_length); builder.previous_kernel.set_unencrypted_logs( diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr index 9514f4dd333..9f47aa645f1 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr @@ -12,7 +12,7 @@ use dep::types::{ MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, MAX_PUBLIC_DATA_READS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_CALL, NUM_FIELDS_PER_SHA256, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_NON_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX }, @@ -433,7 +433,7 @@ fn propagate_new_l2_to_l1_messages(public_call: PublicCallData, public_inputs: & */ pub fn accumulate_unencrypted_logs( public_call: PublicCallData, - previous_unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + previous_unencrypted_logs_hash: Field, previous_unencrypted_log_preimages_length: Field, public_inputs: &mut PublicKernelCircuitPublicInputsBuilder ) { @@ -441,8 +441,8 @@ pub fn accumulate_unencrypted_logs( let current_unencrypted_logs_hash = public_call_public_inputs.unencrypted_logs_hash; public_inputs.end.unencrypted_logs_hash = accumulate_sha256([ - previous_unencrypted_logs_hash[0], - current_unencrypted_logs_hash[0], + previous_unencrypted_logs_hash, + current_unencrypted_logs_hash, ]); // Add log preimages lengths from current iteration to accumulated lengths diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr index e783994c9a8..db9b2311809 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr @@ -295,14 +295,14 @@ mod tests { fn circuit_outputs_should_be_correctly_populated_with_previous_public_kernel_logs() { let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new(); // Logs for the current call stack. - let unencrypted_logs_hash = [26]; + let unencrypted_logs_hash = 26; let unencrypted_log_preimages_length = 50; builder.public_call.set_unencrypted_logs(unencrypted_logs_hash, unencrypted_log_preimages_length); // Logs for the previous call stack. - let prev_encrypted_logs_hash = [80]; + let prev_encrypted_logs_hash = 80; let prev_encrypted_log_preimages_length = 13; - let prev_unencrypted_logs_hash = [956]; + let prev_unencrypted_logs_hash = 956; let prev_unencrypted_log_preimages_length = 24; builder.previous_kernel.set_encrypted_logs(prev_encrypted_logs_hash, prev_encrypted_log_preimages_length); builder.previous_kernel.set_unencrypted_logs( diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr index 1306b26b630..c3003672c8a 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr @@ -453,14 +453,14 @@ mod tests { let mut builder = PublicKernelSetupCircuitPrivateInputsBuilder::new(); // Logs for the current call stack. - let unencrypted_logs_hash = [26]; + let unencrypted_logs_hash = 26; let unencrypted_log_preimages_length = 50; builder.public_call.set_unencrypted_logs(unencrypted_logs_hash, unencrypted_log_preimages_length); // Logs for the previous call stack. - let prev_encrypted_logs_hash = [80]; + let prev_encrypted_logs_hash = 80; let prev_encrypted_log_preimages_length = 13; - let prev_unencrypted_logs_hash = [956]; + let prev_unencrypted_logs_hash = 956; let prev_unencrypted_log_preimages_length = 24; builder.previous_kernel.set_encrypted_logs(prev_encrypted_logs_hash, prev_encrypted_log_preimages_length); builder.previous_kernel.set_unencrypted_logs( diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr index 3f301d0da55..85409d64a6c 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr @@ -343,14 +343,14 @@ mod tests { let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); // Logs for the current call stack. - let unencrypted_logs_hash = [26]; + let unencrypted_logs_hash = 26; let unencrypted_log_preimages_length = 50; builder.public_call.set_unencrypted_logs(unencrypted_logs_hash, unencrypted_log_preimages_length); // Logs for the previous call stack. - let prev_encrypted_logs_hash = [80]; + let prev_encrypted_logs_hash = 80; let prev_encrypted_log_preimages_length = 13; - let prev_unencrypted_logs_hash = [956]; + let prev_unencrypted_logs_hash = 956; let prev_unencrypted_log_preimages_length = 24; builder.previous_kernel.set_encrypted_logs(prev_encrypted_logs_hash, prev_encrypted_log_preimages_length); builder.previous_kernel.set_unencrypted_logs( diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr index bf5b8d528de..15f33302d75 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr @@ -1,5 +1,5 @@ use dep::types::{ - abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, constants::NUM_FIELDS_PER_SHA256, + abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, partial_state_reference::PartialStateReference }; use crate::abis::constant_rollup_data::ConstantRollupData; @@ -26,6 +26,6 @@ struct BaseOrMergeRollupPublicInputs { // So we want to constrain it when casting these fields to U128 // We hash public inputs to make them constant-sized (to then be unpacked on-chain) - txs_effects_hash : [Field; NUM_FIELDS_PER_SHA256], - out_hash : [Field; NUM_FIELDS_PER_SHA256], + txs_effects_hash : Field, + out_hash : Field, } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr index 488343e6e89..d5d537326f5 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -17,7 +17,7 @@ use dep::types::{ }, constants::{ NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, - PUBLIC_DATA_TREE_HEIGHT, NOTE_HASH_SUBTREE_HEIGHT, NUM_FIELDS_PER_SHA256, + PUBLIC_DATA_TREE_HEIGHT, NOTE_HASH_SUBTREE_HEIGHT, MAX_NEW_NOTE_HASHES_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, NUM_ENCRYPTED_LOGS_HASHES_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, NUM_UNENCRYPTED_LOGS_HASHES_PER_TX, NULLIFIER_SUBTREE_HEIGHT, NULLIFIER_TREE_HEIGHT, @@ -407,7 +407,7 @@ mod tests { MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, NOTE_HASH_TREE_HEIGHT, NOTE_HASH_SUBTREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_TREE_HEIGHT, NULLIFIER_SUBTREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, - PUBLIC_DATA_SUBTREE_HEIGHT, PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, NUM_FIELDS_PER_SHA256, + PUBLIC_DATA_SUBTREE_HEIGHT, PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, MAX_NEW_L2_TO_L1_MSGS_PER_TX }, contract_class_id::ContractClassId, partial_state_reference::PartialStateReference, @@ -952,10 +952,8 @@ mod tests { let hash_input_flattened = [0; TX_EFFECTS_HASH_INPUT_FIELDS * 32]; let sha_digest = dep::std::hash::sha256(hash_input_flattened); - let expected_tx_effects_hash = [field_from_bytes_32_trunc(sha_digest)]; - for i in 0..NUM_FIELDS_PER_SHA256 { - assert_eq(outputs.txs_effects_hash[i], expected_tx_effects_hash[i]); - } + let expected_tx_effects_hash = field_from_bytes_32_trunc(sha_digest); + assert_eq(outputs.txs_effects_hash, expected_tx_effects_hash); } #[test] @@ -964,10 +962,8 @@ mod tests { let hash_input_flattened = [0; MAX_NEW_L2_TO_L1_MSGS_PER_TX * 32]; let sha_digest = dep::std::hash::sha256(hash_input_flattened); - let expected_out_hash = [field_from_bytes_32_trunc(sha_digest)]; - for i in 0..NUM_FIELDS_PER_SHA256 { - assert_eq(outputs.out_hash[i], expected_out_hash[i]); - } + let expected_out_hash = field_from_bytes_32_trunc(sha_digest); + assert_eq(outputs.out_hash, expected_out_hash); } #[test] @@ -980,11 +976,8 @@ mod tests { let mut hash_input_flattened = [0; MAX_NEW_L2_TO_L1_MSGS_PER_TX * 32]; hash_input_flattened[MAX_NEW_L2_TO_L1_MSGS_PER_TX * 32 - 1] = 123; let sha_digest = dep::std::hash::sha256(hash_input_flattened); - let expected_out_hash = [field_from_bytes_32_trunc(sha_digest)]; - - for i in 0..NUM_FIELDS_PER_SHA256 { - assert_eq(out_hash[i], expected_out_hash[i]); - } + let expected_out_hash = field_from_bytes_32_trunc(sha_digest); + assert_eq(out_hash, expected_out_hash); } #[test(should_fail_with = "membership check failed")] diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr index 40b95332811..5d54cf35149 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr @@ -3,7 +3,7 @@ use crate::abis::previous_rollup_data::PreviousRollupData; use dep::types::{ mocked::AggregationObject, hash::accumulate_sha256, constants::{ - NUM_FIELDS_PER_SHA256, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, + MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, NUM_UNENCRYPTED_LOGS_HASHES_PER_TX, NUM_ENCRYPTED_LOGS_HASHES_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX }, @@ -79,24 +79,22 @@ pub fn assert_prev_rollups_follow_on_from_each_other( ); } -// TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - /** * @brief From two previous rollup data, compute a single out hash * * @param previous_rollup_data * @return out hash stored in 2 fields */ -pub fn compute_out_hash(previous_rollup_data: [PreviousRollupData; 2]) -> [Field; NUM_FIELDS_PER_SHA256] { +pub fn compute_out_hash(previous_rollup_data: [PreviousRollupData; 2]) -> Field { accumulate_sha256( [ - previous_rollup_data[0].base_or_merge_rollup_public_inputs.out_hash[0], - previous_rollup_data[1].base_or_merge_rollup_public_inputs.out_hash[0], + previous_rollup_data[0].base_or_merge_rollup_public_inputs.out_hash, + previous_rollup_data[1].base_or_merge_rollup_public_inputs.out_hash, ] ) } -pub fn compute_kernel_out_hash(combined: CombinedAccumulatedData) -> [Field; NUM_FIELDS_PER_SHA256] { +pub fn compute_kernel_out_hash(combined: CombinedAccumulatedData) -> Field { let mut out_hash_inputs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX] = combined.new_l2_to_l1_msgs; let mut hash_input_flattened = [0; MAX_NEW_L2_TO_L1_MSGS_PER_TX * 32]; @@ -108,7 +106,7 @@ pub fn compute_kernel_out_hash(combined: CombinedAccumulatedData) -> [Field; NUM } let sha_digest = dep::types::hash::sha256_to_field(hash_input_flattened); - [sha_digest] + sha_digest } /** @@ -117,11 +115,11 @@ pub fn compute_kernel_out_hash(combined: CombinedAccumulatedData) -> [Field; NUM * @param previous_rollup_data * @return The hash of the transaction effects stored in 2 fields */ -pub fn compute_txs_effects_hash(previous_rollup_data: [PreviousRollupData; 2]) -> [Field; NUM_FIELDS_PER_SHA256] { +pub fn compute_txs_effects_hash(previous_rollup_data: [PreviousRollupData; 2]) -> Field { accumulate_sha256( [ - previous_rollup_data[0].base_or_merge_rollup_public_inputs.txs_effects_hash[0], - previous_rollup_data[1].base_or_merge_rollup_public_inputs.txs_effects_hash[0], + previous_rollup_data[0].base_or_merge_rollup_public_inputs.txs_effects_hash, + previous_rollup_data[1].base_or_merge_rollup_public_inputs.txs_effects_hash, ] ) } @@ -130,7 +128,7 @@ global TX_EFFECTS_HASH_INPUT_FIELDS = 197; // Computes the tx effects hash for a base rollup (a single transaction) // TODO(Alvaro): This is too slow for brillig without the array optimization -pub fn compute_tx_effects_hash(combined: CombinedAccumulatedData) -> [Field; NUM_FIELDS_PER_SHA256] { +pub fn compute_tx_effects_hash(combined: CombinedAccumulatedData) -> Field { // Compute tx effect hash // Consist of // MAX_NEW_NOTE_HASHES_PER_TX fields for note hashes @@ -179,17 +177,13 @@ pub fn compute_tx_effects_hash(combined: CombinedAccumulatedData) -> [Field; NUM } offset += MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2; - for j in 0..NUM_FIELDS_PER_SHA256 { - txs_effects_hash_input[offset + j] = encrypted_logs_hash[j]; - } + txs_effects_hash_input[offset] = encrypted_logs_hash; - offset += NUM_ENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256; + offset += NUM_ENCRYPTED_LOGS_HASHES_PER_TX; - for j in 0..NUM_FIELDS_PER_SHA256 { - txs_effects_hash_input[offset + j] = unencrypted_logs_hash[j]; - } + txs_effects_hash_input[offset] = unencrypted_logs_hash; - offset += NUM_UNENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256; + offset += NUM_UNENCRYPTED_LOGS_HASHES_PER_TX; assert_eq(offset, TX_EFFECTS_HASH_INPUT_FIELDS); // Sanity check let mut hash_input_flattened = [0; TX_EFFECTS_HASH_INPUT_FIELDS * 32]; @@ -201,7 +195,7 @@ pub fn compute_tx_effects_hash(combined: CombinedAccumulatedData) -> [Field; NUM } let sha_digest = dep::types::hash::sha256_to_field(hash_input_flattened); - [sha_digest] + sha_digest } #[test] @@ -212,7 +206,7 @@ fn consistent_TX_EFFECTS_HASH_INPUT_FIELDS() { + MAX_NEW_NULLIFIERS_PER_TX + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2 + MAX_NEW_L2_TO_L1_MSGS_PER_TX - + NUM_ENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256 - + NUM_UNENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256; + + NUM_ENCRYPTED_LOGS_HASHES_PER_TX + + NUM_UNENCRYPTED_LOGS_HASHES_PER_TX; assert(TX_EFFECTS_HASH_INPUT_FIELDS == expected_size, "tx effects hash input size is incorrect"); } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root.nr index 2a7dbfa4c9b..3784e95d63b 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root.nr @@ -6,7 +6,7 @@ use root_rollup_inputs::RootRollupInputs; use root_rollup_public_inputs::RootRollupPublicInputs; // TODO: Move all the following code to different files -use dep::types::{constants::{NUM_FIELDS_PER_SHA256, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP}, utils::uint256::U256, hash::sha256_to_field}; +use dep::types::{constants::NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, utils::uint256::U256, hash::sha256_to_field}; // See `test_message_input_flattened_length` on keeping this in sync, // why its here and how this constant was computed. @@ -16,7 +16,7 @@ global NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP_NUM_BYTES: u64 = 512; // // TODO(Miranda): remove? This appears to be unused // Returns the hash truncated to one field element -fn compute_messages_hash(leaves: [Field; NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP]) -> [Field; NUM_FIELDS_PER_SHA256] { +fn compute_messages_hash(leaves: [Field; NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP]) -> Field { // Slice variation // let mut hash_input_flattened = []; // for leaf in leaves { @@ -36,7 +36,7 @@ fn compute_messages_hash(leaves: [Field; NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP]) - } } - [sha256_to_field(hash_input_flattened)] + sha256_to_field(hash_input_flattened) } #[test] diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_inputs.nr index f14909664eb..4e87655b8f8 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_inputs.nr @@ -61,7 +61,7 @@ impl RootRollupInputs { let content_commitment = ContentCommitment { tx_tree_height: right.height_in_block_tree + 1, txs_effects_hash: components::compute_txs_effects_hash(self.previous_rollup_data), - in_hash: [self.l1_to_l2_roots.public_inputs.sha_root], + in_hash: self.l1_to_l2_roots.public_inputs.sha_root, out_hash: components::compute_out_hash(self.previous_rollup_data) }; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tests/previous_rollup_data.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tests/previous_rollup_data.nr index 9ef6c6920ee..3185a6bfdb7 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tests/previous_rollup_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/tests/previous_rollup_data.nr @@ -62,11 +62,11 @@ pub fn default_previous_rollup_data() -> [PreviousRollupData; 2] { previous_rollup_data[0].base_or_merge_rollup_public_inputs.height_in_block_tree = 1; previous_rollup_data[1].base_or_merge_rollup_public_inputs.height_in_block_tree = 1; - previous_rollup_data[0].base_or_merge_rollup_public_inputs.txs_effects_hash = [1]; - previous_rollup_data[1].base_or_merge_rollup_public_inputs.txs_effects_hash = [2]; + previous_rollup_data[0].base_or_merge_rollup_public_inputs.txs_effects_hash = 1; + previous_rollup_data[1].base_or_merge_rollup_public_inputs.txs_effects_hash = 2; - previous_rollup_data[0].base_or_merge_rollup_public_inputs.out_hash = [1]; - previous_rollup_data[1].base_or_merge_rollup_public_inputs.out_hash = [2]; + previous_rollup_data[0].base_or_merge_rollup_public_inputs.out_hash = 1; + previous_rollup_data[1].base_or_merge_rollup_public_inputs.out_hash = 2; previous_rollup_data } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/accumulated_revertible_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/accumulated_revertible_data_builder.nr index 738b2b7706d..232ebbc81bc 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/accumulated_revertible_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/accumulated_revertible_data_builder.nr @@ -9,7 +9,7 @@ use crate::{ } }; use crate::constants::{ - MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, NUM_FIELDS_PER_SHA256, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_REVERTIBLE_NOTE_HASHES_PER_TX, MAX_REVERTIBLE_NULLIFIERS_PER_TX, MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX }; @@ -21,9 +21,9 @@ struct AccumulatedRevertibleDataBuilder { private_call_stack: BoundedVec, public_call_stack: BoundedVec, new_l2_to_l1_msgs: BoundedVec, - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - encrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + + encrypted_logs_hash: Field, + unencrypted_logs_hash: Field, // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the // variable-length data. diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data.nr index 8c8ac82db33..76f7e00531f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data.nr @@ -12,7 +12,7 @@ use crate::{ use crate::constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NUM_FIELDS_PER_SHA256 + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX }; use dep::std::unsafe; @@ -29,9 +29,9 @@ struct CombinedAccumulatedData { private_call_stack: [CallRequest; MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX], public_call_stack: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], new_l2_to_l1_msgs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX], - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - encrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + + encrypted_logs_hash: Field, + unencrypted_logs_hash: Field, // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the // variable-length data. diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data_builder.nr index 53aad2456a3..f72d34c9565 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/combined_accumulated_data_builder.nr @@ -16,7 +16,7 @@ use crate::{ use crate::constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NUM_FIELDS_PER_SHA256 + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX }; use dep::std::unsafe; @@ -33,9 +33,9 @@ struct CombinedAccumulatedDataBuilder { private_call_stack: BoundedVec, public_call_stack: BoundedVec, new_l2_to_l1_msgs: BoundedVec, - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - encrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + + encrypted_logs_hash: Field, + unencrypted_logs_hash: Field, // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the // variable-length data. diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_revertible_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_revertible_data.nr index 8c6874e6749..9144bd52a22 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_revertible_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_revertible_data.nr @@ -2,7 +2,7 @@ use crate::{abis::{call_request::CallRequest, side_effect::{SideEffect, SideEffe use crate::constants::{ MAX_REVERTIBLE_NOTE_HASHES_PER_TX, MAX_REVERTIBLE_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_NEW_L2_TO_L1_MSGS_PER_TX, NUM_FIELDS_PER_SHA256 + MAX_NEW_L2_TO_L1_MSGS_PER_TX }; struct PrivateAccumulatedRevertibleData { @@ -12,9 +12,9 @@ struct PrivateAccumulatedRevertibleData { private_call_stack: [CallRequest; MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX], public_call_stack: [CallRequest; MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX], new_l2_to_l1_msgs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX], - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - encrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + + encrypted_logs_hash: Field, + unencrypted_logs_hash: Field, // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the // variable-length data. diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_revertible_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_revertible_data.nr index 777457eb4dd..3b3c21a9067 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_revertible_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/public_accumulated_revertible_data.nr @@ -7,7 +7,7 @@ use crate::{ use crate::constants::{ MAX_REVERTIBLE_NOTE_HASHES_PER_TX, MAX_REVERTIBLE_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_NEW_L2_TO_L1_MSGS_PER_TX, NUM_FIELDS_PER_SHA256, + MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX }; @@ -18,9 +18,9 @@ struct PublicAccumulatedRevertibleData { private_call_stack: [CallRequest; MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX], public_call_stack: [CallRequest; MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX], new_l2_to_l1_msgs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX], - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - encrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + + encrypted_logs_hash: Field, + unencrypted_logs_hash: Field, // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the // variable-length data. diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr index 6e9d54b4b08..331bb7ec6a6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr @@ -8,7 +8,7 @@ use crate::{ MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, NUM_FIELDS_PER_SHA256, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, RETURN_VALUES_LENGTH, PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH, GENERATOR_INDEX__PRIVATE_CIRCUIT_PUBLIC_INPUTS }, @@ -38,9 +38,8 @@ struct PrivateCircuitPublicInputs { start_side_effect_counter : u32, end_side_effect_counter : u32, - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - encrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + encrypted_logs_hash: Field, + unencrypted_logs_hash: Field, // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the // variable-length data. @@ -116,8 +115,8 @@ impl Serialize for PrivateCircuitPublicInp } fields.push(self.start_side_effect_counter as Field); fields.push(self.end_side_effect_counter as Field); - fields.extend_from_array(self.encrypted_logs_hash); - fields.extend_from_array(self.unencrypted_logs_hash); + fields.push(self.encrypted_logs_hash); + fields.push(self.unencrypted_logs_hash); fields.push(self.encrypted_log_preimages_length); fields.push(self.unencrypted_log_preimages_length); fields.extend_from_array(self.historical_header.serialize()); @@ -150,8 +149,8 @@ impl Deserialize for PrivateCircuitPublicI new_l2_to_l1_msgs: reader.read_struct_array(L2ToL1Message::deserialize, [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL]), start_side_effect_counter: reader.read() as u32, end_side_effect_counter: reader.read() as u32, - encrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]), - unencrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]), + encrypted_logs_hash: reader.read() as Field, + unencrypted_logs_hash: reader.read() as Field, encrypted_log_preimages_length: reader.read(), unencrypted_log_preimages_length: reader.read(), historical_header: reader.read_struct(Header::deserialize), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr index a243b7a3dfb..dab7f2b7fcf 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr @@ -8,7 +8,7 @@ use crate::{ MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH, + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, RETURN_VALUES_LENGTH, GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS, PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH }, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, @@ -35,8 +35,8 @@ struct PublicCircuitPublicInputs{ start_side_effect_counter: u32, end_side_effect_counter: u32, - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + + unencrypted_logs_hash: Field, // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the // variable-length data. @@ -90,7 +90,7 @@ impl Serialize for PublicCircuitPublicInput fields.push(self.start_side_effect_counter as Field); fields.push(self.end_side_effect_counter as Field); - fields.extend_from_array(self.unencrypted_logs_hash); + fields.push(self.unencrypted_logs_hash); fields.push(self.unencrypted_log_preimages_length); fields.extend_from_array(self.historical_header.serialize()); fields.push(self.prover_address.to_field()); @@ -117,7 +117,7 @@ impl Deserialize for PublicCircuitPublicInp new_l2_to_l1_msgs: reader.read_struct_array(L2ToL1Message::deserialize, [L2ToL1Message::empty(); MAX_NEW_L2_TO_L1_MSGS_PER_CALL]), start_side_effect_counter: reader.read() as u32, end_side_effect_counter: reader.read() as u32, - unencrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]), + unencrypted_logs_hash: reader.read() as Field, unencrypted_log_preimages_length: reader.read(), historical_header: reader.read_struct(Header::deserialize), prover_address: reader.read_struct(AztecAddress::deserialize), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index fee1800d164..67ed52200d9 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -96,8 +96,6 @@ global L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH: u64 = 12; // MISC CONSTANTS global FUNCTION_SELECTOR_NUM_BYTES: Field = 4; -// sha256 hash is truncated into a single field -global NUM_FIELDS_PER_SHA256: u64 = 1; global ARGS_HASH_CHUNK_LENGTH: u64 = 32; global ARGS_HASH_CHUNK_COUNT: u64 = 32; // The following is used in immutable state variables to compute an initialization slot whose value is used to diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/content_commitment.nr b/noir-projects/noir-protocol-circuits/crates/types/src/content_commitment.nr index 5c0061d4e01..57ae95f1199 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/content_commitment.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/content_commitment.nr @@ -1,23 +1,23 @@ use crate::{ - constants::{NUM_FIELDS_PER_SHA256, CONTENT_COMMITMENT_LENGTH}, + constants::CONTENT_COMMITMENT_LENGTH, traits::{Deserialize, Empty, Hash, Serialize}, utils::{arr_copy_slice} }; -// TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 + struct ContentCommitment { tx_tree_height: Field, - txs_effects_hash: [Field; NUM_FIELDS_PER_SHA256], - in_hash: [Field; NUM_FIELDS_PER_SHA256], - out_hash: [Field; NUM_FIELDS_PER_SHA256], + txs_effects_hash: Field, + in_hash: Field, + out_hash: Field, } impl Serialize for ContentCommitment { fn serialize(self) -> [Field; CONTENT_COMMITMENT_LENGTH] { let mut fields: BoundedVec = BoundedVec::new(); - fields.extend_from_array([self.tx_tree_height]); - fields.extend_from_array(self.txs_effects_hash); - fields.extend_from_array(self.in_hash); - fields.extend_from_array(self.out_hash); + fields.push(self.tx_tree_height); + fields.push(self.txs_effects_hash); + fields.push(self.in_hash); + fields.push(self.out_hash); fields.storage } @@ -26,15 +26,12 @@ impl Serialize for ContentCommitment { impl Deserialize for ContentCommitment { fn deserialize(serialized: [Field; CONTENT_COMMITMENT_LENGTH]) -> Self { let tx_tree_height = serialized[0]; - let mut offset = 1; - let txs_effects_hash = arr_copy_slice(serialized, [0; NUM_FIELDS_PER_SHA256], offset); - offset = offset + NUM_FIELDS_PER_SHA256; + let txs_effects_hash = serialized[1]; - let in_hash = arr_copy_slice(serialized, [0; NUM_FIELDS_PER_SHA256], offset); - offset = offset + NUM_FIELDS_PER_SHA256; + let in_hash = serialized[2]; - let out_hash = arr_copy_slice(serialized, [0; NUM_FIELDS_PER_SHA256], offset); + let out_hash = serialized[3]; Self { tx_tree_height, @@ -49,9 +46,9 @@ impl Empty for ContentCommitment { fn empty() -> Self { Self { tx_tree_height: 0, - txs_effects_hash: [0; NUM_FIELDS_PER_SHA256], - in_hash: [0; NUM_FIELDS_PER_SHA256], - out_hash: [0; NUM_FIELDS_PER_SHA256], + txs_effects_hash: 0, + in_hash: 0, + out_hash: 0, } } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr index fbb6996c78d..cc0948346ef 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -6,7 +6,7 @@ use crate::contract_class_id::ContractClassId; use crate::abis::side_effect::{SideEffect}; use crate::utils::{uint256::U256, field::field_from_bytes_32_trunc}; use crate::constants::{ - ARGS_HASH_CHUNK_COUNT, ARGS_HASH_CHUNK_LENGTH, FUNCTION_TREE_HEIGHT, NUM_FIELDS_PER_SHA256, + ARGS_HASH_CHUNK_COUNT, ARGS_HASH_CHUNK_LENGTH, FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__SILOED_NOTE_HASH, GENERATOR_INDEX__OUTER_NULLIFIER, GENERATOR_INDEX__VK, GENERATOR_INDEX__CONSTRUCTOR, GENERATOR_INDEX__PARTIAL_ADDRESS, GENERATOR_INDEX__CONTRACT_ADDRESS, GENERATOR_INDEX__NOTE_HASH_NONCE, GENERATOR_INDEX__UNIQUE_NOTE_HASH, GENERATOR_INDEX__FUNCTION_ARGS @@ -119,9 +119,8 @@ pub fn compute_l2_to_l1_hash( // // TODO(Jan and David): This is used for the encrypted_log hashes. // Can we check to see if we can just use hash_to_field or pedersen_compress here? -// TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 // -pub fn accumulate_sha256(input: [Field; NUM_FIELDS_PER_SHA256 * 2]) -> [Field; NUM_FIELDS_PER_SHA256] { +pub fn accumulate_sha256(input: [Field; 2]) -> Field { // This is a note about the cpp code, since it takes an array of Fields // instead of a U128. // 4 Field elements when converted to bytes will usually @@ -141,17 +140,17 @@ pub fn accumulate_sha256(input: [Field; NUM_FIELDS_PER_SHA256 * 2]) -> [Field; N } } - [sha256_to_field(hash_input_flattened)] + sha256_to_field(hash_input_flattened) } pub fn compute_logs_hash( - previous_log_hash: [Field; NUM_FIELDS_PER_SHA256], - current_log_hash: [Field; NUM_FIELDS_PER_SHA256] -) -> [Field; NUM_FIELDS_PER_SHA256] { + previous_log_hash: Field, + current_log_hash: Field +) -> Field { accumulate_sha256( [ - previous_log_hash[0], - current_log_hash[0] + previous_log_hash, + current_log_hash ] ) } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr index 13b74af3e10..e60f0ec64d7 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/kernel_data_builder.nr @@ -19,7 +19,7 @@ use crate::{ }; use crate::constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NON_REVERTIBLE_NULLIFIERS_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_PUBLIC_DATA_READS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NUM_FIELDS_PER_SHA256, + MAX_PUBLIC_DATA_READS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, VK_TREE_HEIGHT }; use dep::std::unsafe; @@ -235,12 +235,12 @@ impl PreviousKernelDataBuilder { self.min_revertible_side_effect_counter = self.sideffect_counter; } - pub fn set_encrypted_logs(&mut self, hash: [Field; NUM_FIELDS_PER_SHA256], preimages_length: Field) { + pub fn set_encrypted_logs(&mut self, hash: Field, preimages_length: Field) { self.end.encrypted_logs_hash = hash; self.end.encrypted_log_preimages_length = preimages_length; } - pub fn set_unencrypted_logs(&mut self, hash: [Field; NUM_FIELDS_PER_SHA256], preimages_length: Field) { + pub fn set_unencrypted_logs(&mut self, hash: Field, preimages_length: Field) { self.end.unencrypted_logs_hash = hash; self.end.unencrypted_log_preimages_length = preimages_length; } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_call_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_call_data_builder.nr index e19d960d1c9..e3874230c8c 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_call_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_call_data_builder.nr @@ -16,7 +16,7 @@ use crate::{ }; use crate::constants::{ MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, - MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256 + MAX_NOTE_HASH_READ_REQUESTS_PER_CALL }; struct PrivateCallDataBuilder { @@ -155,12 +155,12 @@ impl PrivateCallDataBuilder { self.note_hash_read_request_membership_witnesses.extend_from_bounded_vec(read_request_membership_witnesses); } - pub fn set_encrypted_logs(&mut self, hash: [Field; NUM_FIELDS_PER_SHA256], preimages_length: Field) { + pub fn set_encrypted_logs(&mut self, hash: Field, preimages_length: Field) { self.public_inputs.encrypted_logs_hash = hash; self.public_inputs.encrypted_log_preimages_length = preimages_length; } - pub fn set_unencrypted_logs(&mut self, hash: [Field; NUM_FIELDS_PER_SHA256], preimages_length: Field) { + pub fn set_unencrypted_logs(&mut self, hash: Field, preimages_length: Field) { self.public_inputs.unencrypted_logs_hash = hash; self.public_inputs.unencrypted_log_preimages_length = preimages_length; } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr index 215d7bb6745..ab547e642d1 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr @@ -12,7 +12,7 @@ use crate::constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, NUM_FIELDS_PER_SHA256, + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, RETURN_VALUES_LENGTH }; @@ -36,9 +36,9 @@ struct PrivateCircuitPublicInputsBuilder { private_call_stack_hashes: BoundedVec, public_call_stack_hashes: BoundedVec, new_l2_to_l1_msgs: BoundedVec, - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - encrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + + encrypted_logs_hash: Field, + unencrypted_logs_hash: Field, encrypted_log_preimages_length: Field, unencrypted_log_preimages_length: Field, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr index f10579958e5..e71ac955020 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_call_data_builder.nr @@ -10,7 +10,7 @@ use crate::{ }; use crate::constants::{ MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256 + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL }; struct PublicCallDataBuilder { @@ -147,7 +147,7 @@ impl PublicCallDataBuilder { } } - pub fn set_unencrypted_logs(&mut self, hash: [Field; NUM_FIELDS_PER_SHA256], preimages_length: Field) { + pub fn set_unencrypted_logs(&mut self, hash: Field, preimages_length: Field) { self.public_inputs.unencrypted_logs_hash = hash; self.public_inputs.unencrypted_log_preimages_length = preimages_length; } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr index 169cfb4bcd4..c033915b4b9 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr @@ -11,7 +11,7 @@ use crate::constants::{ MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, RETURN_VALUES_LENGTH }; struct PublicCircuitPublicInputsBuilder { @@ -28,8 +28,7 @@ struct PublicCircuitPublicInputsBuilder { new_l2_to_l1_msgs: BoundedVec, start_side_effect_counter: u32, end_side_effect_counter: u32, - // TODO(Miranda): Remove arrays entirely as NUM_FIELDS_PER_SHA256 = 1 - unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], + unencrypted_logs_hash: Field, unencrypted_log_preimages_length: Field, historical_header: Header, prover_address: AztecAddress, diff --git a/yarn-project/circuit-types/src/l2_block.ts b/yarn-project/circuit-types/src/l2_block.ts index e02ea43329b..db167a6640e 100644 --- a/yarn-project/circuit-types/src/l2_block.ts +++ b/yarn-project/circuit-types/src/l2_block.ts @@ -1,8 +1,8 @@ import { Body, TxEffect, TxHash } from '@aztec/circuit-types'; import { AppendOnlyTreeSnapshot, Header, STRING_ENCODING } from '@aztec/circuits.js'; -import { sha256 } from '@aztec/foundation/crypto'; +import { sha256, sha256ToField } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, serializeToBuffer, toTruncField } from '@aztec/foundation/serialize'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { makeAppendOnlyTreeSnapshot, makeHeader } from './l2_block_code_to_purge.js'; @@ -160,7 +160,7 @@ export class L2Block { this.body.getTxsEffectsHash(), ); - return toTruncField(sha256(buf))[0]; + return sha256ToField(buf); } /** diff --git a/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts b/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts index 491ba12e26e..07666712eee 100644 --- a/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts +++ b/yarn-project/circuit-types/src/messaging/l1_to_l2_message.ts @@ -1,6 +1,6 @@ -import { sha256 } from '@aztec/foundation/crypto'; +import { sha256ToField } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, serializeToBuffer, toTruncField } from '@aztec/foundation/serialize'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { L1Actor } from './l1_actor.js'; import { L2Actor } from './l2_actor.js'; @@ -41,7 +41,7 @@ export class L1ToL2Message { } hash(): Fr { - return toTruncField(sha256(serializeToBuffer(...this.toFields())))[0]; + return sha256ToField(serializeToBuffer(...this.toFields())); } static fromBuffer(buffer: Buffer | BufferReader): L1ToL2Message { diff --git a/yarn-project/circuit-types/src/mocks_to_purge.ts b/yarn-project/circuit-types/src/mocks_to_purge.ts index 3e36a9a54f0..60708226bd3 100644 --- a/yarn-project/circuit-types/src/mocks_to_purge.ts +++ b/yarn-project/circuit-types/src/mocks_to_purge.ts @@ -20,7 +20,6 @@ import { MAX_REVERTIBLE_NOTE_HASHES_PER_TX, MAX_REVERTIBLE_NULLIFIERS_PER_TX, MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, - NUM_FIELDS_PER_SHA256, Point, PrivateAccumulatedNonRevertibleData, PrivateAccumulatedRevertibleData, @@ -147,8 +146,8 @@ export function makeFinalAccumulatedData(seed = 1, full = false): PrivateAccumul tupleGenerator(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x400), tupleGenerator(MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x500), tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_TX, fr, seed + 0x600), - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x700), // encrypted logs hash - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x800), // unencrypted logs hash + fr(seed + 0x700), // encrypted logs hash + fr(seed + 0x800), // unencrypted logs hash fr(seed + 0x900), // encrypted_log_preimages_length fr(seed + 0xa00), // unencrypted_log_preimages_length ); diff --git a/yarn-project/circuit-types/src/tx/processed_tx.ts b/yarn-project/circuit-types/src/tx/processed_tx.ts index acaa3ad8083..9e321fb0cee 100644 --- a/yarn-project/circuit-types/src/tx/processed_tx.ts +++ b/yarn-project/circuit-types/src/tx/processed_tx.ts @@ -14,7 +14,7 @@ import { ValidationRequests, makeEmptyProof, } from '@aztec/circuits.js'; -import { Tuple, toTruncField } from '@aztec/foundation/serialize'; +import { Tuple } from '@aztec/foundation/serialize'; /** * Represents a tx that has been processed by the sequencer public processor, @@ -193,8 +193,8 @@ export function toTxEffect(tx: ProcessedTx): TxEffect { function validateProcessedTxLogs(tx: ProcessedTx): void { const unencryptedLogs = tx.unencryptedLogs || new TxL2Logs([]); - const kernelUnencryptedLogsHash = tx.data.combinedData.unencryptedLogsHash[0]; - const referenceHash = toTruncField(unencryptedLogs.hash())[0]; + const kernelUnencryptedLogsHash = tx.data.combinedData.unencryptedLogsHash; + const referenceHash = Fr.fromBuffer(unencryptedLogs.hash()); if (!referenceHash.equals(kernelUnencryptedLogsHash)) { throw new Error( `Unencrypted logs hash mismatch. Expected ${referenceHash.toString()}, got ${kernelUnencryptedLogsHash.toString()}. diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index d3349ae4cd9..7e9e09877ef 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -58,7 +58,6 @@ export const PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH = 35; export const L1_TO_L2_MSG_SUBTREE_HEIGHT = 4; export const L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = 12; export const FUNCTION_SELECTOR_NUM_BYTES = 4; -export const NUM_FIELDS_PER_SHA256 = 1; export const ARGS_HASH_CHUNK_LENGTH = 32; export const ARGS_HASH_CHUNK_COUNT = 32; export const INITIALIZATION_SLOT_SEPARATOR = 1000_000_000; diff --git a/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap b/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap index 0e4618e6a8d..3b6fb55754f 100644 --- a/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap +++ b/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap @@ -9,18 +9,18 @@ exports[`ContractClass creates a contract class from a contract compilation arti "selector": { "value": 2381782501 }, - "bytecode": "0x1f8b08000000000000ffed9d777414c7dae65b2030782c01ced9c2e13a60632144163038e78c8d31c62004061b104118e76c72744ee49c93c91803c6d8d737e7ec74af6fd87376bf3dbb7fecf79dddf56ed74cbd9f1e8aea4123770d8f34d5e794a6fa5575bfbf7afaedea54dd5510a4a77f85a940e79b86e9c2e0c849fe9fd4bfa5df6fea10e3ba4a5d72163410ce260d84b36903e12c6c209ccd1a0867f306c2795c03e16cd140385bc6c859007cae788f6f60bc8906c67b42d030e2b6a88170163710ce560d84b37503e16cd340384f6c209c273510ce931b08e7290d84f3d406c2795a03e13cbd81709ed14038cf6c209c673510ceb31b08e7390d84f3dc06c2795e03e12c69209c6d1b08e7f90d84f3821839db01a7dccbbf48fffe40ff5eac7f2fd1bf97eadfcbf46f3b5dc7423d7f7998ae0853fb305d69fc4f09a36ee89785a9a3f1bff230750a53e73075d1ff2bd1ffeb1aa66e61ea1ea61e61aa0853cf30f50a536fad479f305d15a6abc3744d98ae0dd37561ba3e4c3784e9c630dd14a69bc3744b986e0dd36d61ba3d4c7784e9ce30dd15a6bbc3744f98fa86e9de30dd67b0f40bd3fd61ea1fa607c234204c0f866960981e0ad3a0300d0e5365988684a92a4c43c3342c4c0f876978984684e991303d1aa691611a15a6d161aa0ed398308d0dd3b8308d0f534d982684e9b1304d34347b3c4c4f84e9c9303d65703e1da667c2f46c989e0bd3f3617a214c2f86e9a530bd1ca657c234294c93c334254c53c3342d4cd3c334234c33c3342b4cb3c334274c73c3f46a985e0bd3eb617a234c6f86e9ad30bd1da677c2f46e98ded32cb223bc1fa679619a1fa605615a18a645615a1ca625615a1aa665615a1ea615615a19a655615a1da635615a1ba675615a1fa60d61da18a64d61da1ca62d61fa204c5bc3b42d4cdbc3b4234c3bc3b42b4cbbc3b4274c1f86696f983e0ad3be30ed0fd381307d1ca68361fa244c87c2f469983e0bd30fc3f479987e6468fee330fd244c3f0dd3cfb4ede7faf717baacecf3bfd4bfbfd2bfbfd6bfbfd1bfbf35caffce98ffbd31ff07fdfb47fdfb27fdfb67fdfb17fdfb85fefd52ff7ea57fbfd6bfdfe8dfbfeadfbfe9df6ff5efdff5ef3ff4ef3ff5af7afef75edb74be45503b258398daa4f2a183d5fd7f11db7cbea89e5d35d5ff93df126d2fd4f3f25ba0edcdf47c33c3de5ccf3737d6d342cfb730ecc57abed8b0b7d6f3ad0dfb897afe44c37eb29e3fd9b0b7d5f36dc19ed0ff4b572cfda36c4db5a9006c129f4dc0d64cdb9a82adb9ac0e6cc7695b33b0c9f66d0eb696da761cd88ed7b616604b685b4bd1324c27685b32882b564a07abf516c5bd5efdcca4387ede4ab5de568e785bc7cf3b44adb78d035e151f27ea751541dc9ca46dc5603b59db5a81ed146d6b0db653b5ad0dd84ed3b613c176bab69d04b633b4ed64b09da96da780ed2c6d3b156c676bdb69603b47db4e07dbb9da7606d8ced3b633c156a26d67814d37b9c1d9603b5fdbce01db05da762ed82ed4b6f3c026e778256093f3bdb6609373bff3c126e78117689b6a3b5a16803f6d97762be54fda6cb0fd40da6bb05d2c6d35d82e91761a6c97826fb15d066d8dd8da699bb45bea7fdd753e19c4b59f94a5f6931e71af375cb35a6fcff8d79b7a7ed72ba8d53a097e7a8056bd753ec6be2d1dd0b79cdb881fb11742fe26282be5440f39f608bb3a1654e87cef0ccb7537962b86321596fa278378ebdfd3e0e969303783bc9b98ed58ea63b6ce53d631db0fca9ab127e7418d31666f050e07315bee63b6ce53d6313b0cca9ab127e7c28d31661f000e0731dbd54dcc9695fa984ddf1b0b027becc9f550638cd911c0117fcc76f2315bf729eb987d11ca9ab127d7c48d3166270247fc31dba5ab3f37a8f39475ccce81b266ecc9fd99c618b3af008783981de2dbd93a4f59c7ec7c286bc69edc2b6c8c31fb1a70c41fb3dd1cc56c471fb341fa396710d8634fee5b37c6985d041cf1c7ec107f7fb6ee53d631bb0bca9ab127cf501a63cc6ed079f59ce1e7fa39c3d960fb85b69d03bcf1c7765599a3d8eee0633bddff2308ec312acff31a636c7fa4f32a8e7f05fd11c4f66be9ab00b6df68dbf960fbadb65d00f572b00f74f1fb40ddeb94ed3ef067286bc6b23c5b6e8cfbc02f80c341cc76f5315bf73a651bb3ff05ca9ab127fd1c1a63cc7e091c0e62b69b8fd9bad729db98fd77286bc6de253adf1863f6bfeabc3a5ff8a33e5fb80c6c7fd2b67660fbb3b65d0eb6bf68db1560fb42dbda83ed4b6dbb126c5f695b29d8bed6b60e60fb46dbcac0f6576deb08b6bf695b39d8bed5b64e60fbbbb67506db3fb4ad0bd8fea96d5dc1f62f6deba66daa9f9ef4bd3aa46d2d803f19c4bb6da5dfa5ac5be63be4c0772bc377ab1cfa6e63f86e63f15de6c077027cc85460cc27215fe696a7b41878d05779fcbe3aaaba770cea5ef772e0e9e4a0ee09f051179e4ec0d3397e9ed4f1b34bfceb4d6de38e86a609f0d511ead5d541bd0ac097ac5be6c55f31d8b06ded6a61ec163f635901f89275cb7c3760141bb6f5f22e95ec3fea787871412daf837d29754e24fee4db55c2510e7629d3a16d2d5b3bcd5604ffc7e35e67c3e6282e537121be64dd322ffe8aa03e9d73cf585657c64e06a3ab36a2007cc9babdefdaed20793c8ebbb8d6b1b569e2bb470e7c77337c971bbeb1ed9429d3b1ad1b30c77ecda98f6d15f1afb714af4fe4da50fce0f9035ec3c55527f42dd786e247ec8590bfada0b6ac94133da41d167615cbb22d91dd5caeabb15c3194e961a97f3288b7fe15064f85c1acb6c9d5702c74b03fa462a087c121f3e5a05d4584763d403b29730968e7aa3deb6ef0c87c67e09176ac0bf0b8ba268ae2c9c5f5d8d17ce3392c5e3fcbfff13cc0d5f6ea6030cabc6d7b750346dbb98a83eb998ce72a9d80516cdd81a7a323cda2b66b4712df0e6225d51e890f393797fdb733d8a54c37fd429d6a2b1f81b6d2458c603cca54d76bdef8b75359ea1abc53163cb8ed1c5c577570148fa578ffe6bb20de5833dba54e865651f7785cb5e51d0d1e99177f9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec99f999f17912f6db92729d4918cdfe6faeeef3a7be61a8d725eb57cf75feb7d3fe6065a5d83f46fa415c66d4b910ca1435a92dfbffa03f98f99c0afb487676ab5d6a5b627fcc24cc8b3fec6b85db92a1bf53496cbed3dfa274f1bc4df5b156dfd134fb7e76b668eaa2ff336a5a60688afdf12f3778549c5634ad6573f1ec2fdb6791a895e4e37cb6571cd8633dfeed5276d833eb26c1e1ed071e675cf5df91b65a9e97f7307c174299b39ad46e1be95b25e3cc763496c37e3fb26e59e632b05718eb6ead97158e66c6fabbc0b252e65c6853f737a9d5cc415b59966ddf757c6e1eff7138fd1cbf63163c1d80c7453be3e87ce3b0efadc6fd1cdfec9f663b8f9132d8b7cf41bfca8cfd9dc49f67f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b667e66c5633e6bc5f76bcb491873d4f721f53c43be5526eb4f7d2fb849ad5fd7cfe1e499533ba3cef8eee87f6f525b768bce170547f67788da96aebe4f11b52dc51f7e7b069f05b978ae5b00be64dd65162d245f129bef32676343c873fc8e86aee5164d5dedaff88c1535c5fdb593c183cf46a3beed5366d85cf61d8a8a0bf187fb5219d8248fef47bbd8ce782c31fbf5883f7c7efd23ad6debc0d5b62f2b75d96ee0775392c191f18ddf53f939b47dbfd479ecc3817d47beb6fc5fa64ccfa9453f556707dfd72c2d8075c9f6b57ddbb30fb0c6e4bb03aeab40a73e86068590ffaa496d59292765456b6157fb887c0306d9cde53a19cb1543995e96fa278378eb6f7e6bb5b7c1acb6c96f20cebe86e3bfab36a95784469781465206cf835cf5c933db48b37f23f6db6b6e94c1731629f32f68a3a2fa8fdafa1cba3a8e45f539b49d1b770046b38e663fcf7cefa7f51fd05ec4dd4feb3f2086b09f5660acbf1dac5fb89a07d1c71629f37f8df59be7e4b20cf603fbcffe4ff03d97329dcfe69cfc585d5fd9cec971b9a8ba2b66fc0e5a3246668c0964c1f3042973bcd65ab65945047737cbb24511cb8a56e6b7c28a8223f573f39db5f43edfdba88bc4357e835cca9c04757173de923e0774f54db924d449e5cb2c759532a7c3be76a6ce27603be17e7ba9e5ff32653a07c431dcaf8abfcea9ed7b357026c10ffabe065863f2dd017dcb39a0f8117b21e42f695a5b56ca891ea2b5b0ab7d44cea390dd5cae87b15c3194e963a97f3288b7fe57193c5719cc6a9b9c03717629f44377d556f789d0a81d682465f09ea2ed3ba0b6638cabbefd51c798326034db4d3c4eba6433df1332efabd9ce11a48c2c8be7085da09d4d58ca9af70be5781967bf617c57a20bf8c577255c7dbbb93be89684793c2f3896be5d7cc756f98b1a33a17b0e7c478d99900bdf6d0cdf6d72e8db6bee3567d2dcc11804a9f7cff09ba56aca745e8ae312c8724d80d1c5580e89e0f06f8f1f8d11c77790e59a02a38be343b6df3eef028cb25c2130ba78b714c7dfa80b237e63188ff3c2e8e05bb11deafbad58bca7d71c1899ded9c46753c701a38b7ba8f57d570fcfe75bc0afab71893a66c188e7f3b25c4b6074716f1c9f0dd68511af8b64b9e381d1c533ac6cc777c26fcfe3bd65978c998eed8efba294657befa5c22d4fc6730df4ed605cc39416789ff1685af474cb93f1dc077d3bb8ef97d202c7193c9a16f86cd0c5b88789e0f0e77047e3c1e797b2dc89c09874c4d83b0bc62430fee7bd6260ece388319905631f6014fbc9c0e8e0fe6b8ab14f168c789f52963b0518af76c47855168c5703a32c772a30bab8979a00bf7561bc061865b9d380f15a478cd764c1782d30ca72a703e3758e18afcd82f13a6094e5ce00c6eb1d315e9705e3f5c028cb9d098c373862bc3e0bc61b8051963b0b186f74c47843168c3702a32c773630dee488f1c62c186f024659ee1c60bcd911e34d5930de0c8cb2dcb9c0788b23c69bb360bc051865b9f380f156478cb764c1782b30ca7225c0789b23c65bb360bc0d1865b9b6c078bb23c6dbb260bc1d1865b9f381f10e478cb767c1780730ca721700e39d8e18efc882f14e6094e52e04c6bb1c31de9905e35dc028cb5d048c773b62bc2b0bc6bb815196fb0130dee388f1ee2c18ef01c6bb2d8c7d1d31de9305635f6094e52e07c67be3674c5d4bf7cd82f15ee0b92f7e9e9466f766c1739f5b9ed477f5eeb5f8ba3f7e5fa96dd12fa87bddef079efef1f3a4b6c5fd59f00843312c879a3d103f634ab3fe59303e003c03e2e74969f640163c0340b3072c9a3d183f634ab30159303e083c03e3e74969f660163c0341b3072d9a3d143f634ab38159303e043c83e2e74969f650163c83825acd1eb26836387ec6946683b2601c0c3c95f1f3a4341b9c054f256836d8a2d990f819539a5566c1380478aae2e7496936240b9e2ad06c8845b3a1f133a634abca827128f00c8b9f27a5d9d02c78868166432d9a3d1c3f634ab36159303e0c3cc3e3e74969f670163cc341b3872d9a8d889f31a5d9f02c184700cf23f1f3a4341b9105cf23a0d9088b668f3a627c240bc6472d3c717f27fb118baf518eea3e32a87bdd85a11896c37e12a31d318eca82713430ca72d84fa2da11e3e82c18ab8151964b3866ccd44fa21a7c8f89df77aa5daa0eeaaecf18b73c19fb49a0efb18eb41813d45d8bb16e7932f69340dfe31c693136a8bb16e38067bc032d12e0a32e3cc2500ccb613f891a478ce3b360ac0146590efb494c70c4589305e3046094e5b09fc4638e182764c1f81830ca72d84f62a223c6c7b2609c088cb21cf69378dc11e3c42c181f0746590efb493ce188f1f12c189f0046590efb493ce988f1892c189f0446590efb493ce588f1c92c189f0246590efb493ced88f1a92c189f0646590efb493ce388f1e92c189f0146590efb493ceb88f1992c189f0546590efb493ce788f1d92c189f0346590efb493cef88f1b92c189f0746590efb49bce088f1f92c185f004659ee51c78c99ae5f5e68e4bea3ae551abbefa8eb92c6eedbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe73ece997cbfe8c077027cc85460cc27212f0cc5b0dca39eb15133224f497c3ca55877f4f51241dd5fb2f01438aa3bfa7a99a0eec2d0d0185f6c008c8f360046af63ba0f627d1815cf2bf0ff648c3c2f67c1f30af04c72c4f34a163c93806772fc3ca9989a94058f3014c3728f3600c6171b00a3d7d1ebc8c4e875cc1f1d3da367f48c9ef15830368436dc333688782cab2fa3e299123f4f4ab3c959f04c01cd64b9fb1a00e34b0d80f145b78c65f565543c53e3e7496936250b9ea9a0992c779f5bc6b2fa322a9e69f1f3a4349b9a05cf34d06caa4533078c65f565543cd3e3e74969362d0b9ee9a0d9348b660e18cbeacba87866c4cf93d26c7a163c3340b3e916cd1c3096d59751f1cc8c9f27a5d98c2c78668266332c9a39602cab2fa3e299153f4f4ab39959f0cc02cd665a3473c058565f46c5333b7e9e9466b3b2e0990d9acdb268e680b1acbe8c8a674efc3c29cd6667c13307349b6dd18c95f1d106c0f862036074ac63597d1915cf5c473c73b2e0990b3caf3ae2999b05cfabc0f35afc3ca9987a350b1e612886e51e6d008c2f360046afa3d79189d1eb983f3a7a46cfe819b3637ca90130fa6ded1959191d5c5f657c17e9d546ee3bea5da4c6ee3bea5da4c6eedbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe73ecef3c1b78f731fe7f9e0dbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe73ecef3c1b78f731fe7f9e0dbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe73ece997cbf1ebfefb26cbf31f33af0b8f8e68da37a96aaf5bea1d7f55d8cfa29adde34b47ad5d0aa18cabc01fabde940bf02f02beb9679f1972df3c504cc8e7c97a9f6a525d45f7cbc68e8a1fcbfe5a8ee516dfd5b8ddc77545bdfd87d47b5f58dddb78f731fe7f9e0dbc7b98ff37cf0ede3dcc7398b6fcc370b6acfdbe5fba76a1d6feb7ca19e97f22f815dca3c755cfab775e0f72117befd3ee48f15f9e0dbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe77c715e0cff1f9b039ec0e00932f04c21e3a922e3e947c67333194f6f329eb9643c8f93f17425e31949c6f31019cf5d643c2f90f15c47c6f31c194f07329ef1643c53c9788691f1f427e3b9958ca70f19cf93643c5790f17426e3194dc633988ce71e329e52329e57c8786e20e3c9c5fb4bd9f0b427e3a920e39940c6338d8c673819cfa5643c03c8782e21e3b99d8ce76a329e62329e56643ce5643c4f93f18c21e31942c6732f19cf4d643cbdc878e690f14c24e3e946c6339d8ce711329e49643c03c978ee24e3b9968ce732329e67c978cac8789a92f18c23e3194ac6733f194f3b329e5bc87892643c4f90f17421e39941c6338a8c671019cfdd643c2f93f15c4fc6f33c194f0f329e1a329e87c9781e20e3b9988ce736329eabc8784e20e32922e3e944c6f31419cf4c329e6a329ec9643c95643c7dc9786e24e3e949c6339b8ce731329eee643c23c8781e24e3b9838ce71a329e2bc9789e21e3694dc6d3868ca72319cf2c329e02029e4470e4182609f8ffeb606b622cab3efb5ad3b6f6ffef687b1358e65d9d6f6a59f73b60936fc9be6b5916757a07ea92d4f9d2ef37a574425f4998177f45c0f12e09cf2c329e8e643c6dc8785a93f13c43c6732519cf35643c7790f13c48c633828ca73b19cf63643cb3c9787a92f1dc48c6d3978ca7928c6732194f3519cf4c329ea7c8783a91f11491f19c40c6731519cf6d643c1793f13c40c6f330194f0d194f0f329ee7c978ae27e379998ce76e329e41643ca3c8786690f17421e379828c2749c6730b194f3b329efbc9788692f18c23e3694ac65346c6f32c19cf65643cd792f1dc49c633908c671219cf23643cd3c978ba91f14c24e39943c6d38b8ce726329e7bc9788690f18c21e3799a8ca79c8ca715194f3119cfd5643cb793f15c42c633808c47bee7c9c2339c8c671a19cf04329e0a329ef6643caf93f1dc40c6f30a194f2919cf3d643c83c9784693f17426e3b9828ce749329e3e643cb792f1f427e31946c633958c673c194f07329ee7c878ae23e379818ce72e329e87c8784692f17425e3799c8c672e194f6f329e9bc978fa91f15491f14c21e3196be179dd118fbcef2eeb96f9d7497c3bd80ea56abdef39aad3fb7a5dcdf47a855ffc154299c9c7a77fd5fbd8b8ac7099df27c07b71ef8346ef3baa8b6c8f0263fba0efb71df996777e64dd32ff7623f7ddcaf0dd2a4f7cb7317cb7c913df3ece7d9ce7836f1fe73ecef3c1b78f731fe74cbe1d5c1b94e177d2642a30e69390c7eb0517df977354cfc3ae13bf8b513fa5d53c432bf3daaa18cabc07facd73a09fedda53e6c55fb6cc171330635c9404f1c6c5fcf8eb54a6daad96a0eb7c435facd702479a461d43163472df51c790c6ee3bea18d2d87dfb38f7719e0fbe7d9cfb38cf07df3ece7d9c33f95ea8f3315e3796a20ff57c51ae071682dfc53a5f10a35fb5ae457a5d857addc2b118ec52a64922fddb3af0fbbc0bdf7e9ff7c7b67cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ecef9e2dccc4b7ff14b80cd557ffea858ccc5bb04c7d277542c3676df51b1d8d87dfb38f771cee47b8903df09f02153a63e7e4b806791031e47f54c3ddb586ad4e975a34ec550068ff14b1dd4b300fccaba657e29f0c83416785cc4415db639f24c21e3a922e3e947c67333194f6f329eb9643c8f93f17425e31949c6f31019cf5d643c2f90f15c47c6f31c194f07329ef1643c53c9788691f1f427e39947c6732b194f1f329e27c978ae20e3e94cc6339a8c673019cf3d643ca5643caf90f1dc40c6d39e8ca7828c670219cf34329ee1643c9792f10c20e3b99d8ce76a329e62329e56643ce5643c4f93f18c21e31942c6732f19cf4d643cbdc878e690f14c24e3e946c6339d8ce711329e49643c03c978ee24e3b9968ce732329e67c978cac8789a92f18c23e3194ac6733f194f3b329e5bc87892643c4f90f17421e39941c6338a8c671019cfdd643c2f93f15c4fc6f33c194f0f329e1a329e87c9781e20e3b9988ce736329eabc8784e20e32922e3e944c6f31419cf4c329e6a329ec9643c95643c7dc9786e24e3e949c6339b8ce731329eee643c23c8781e24e35940c6730719cf35643c5792f13c43c6d39a8ca70d194f47329e59643c05043c89e0c877ff13f0ff79609377d45f07db329d5f04b626161f4d757e29d80a755ed6715c989e6b7be4ba512757efe5a3af24cc8bbf22e05846c2338b8ca723194f1b329ed6643ccf90f15c49c6730d19cf1d643c0bc8781e24e31941c6d39d8ce731329ed9643c3dc9786e24e3e94bc65349c633998ca79a8c672619cf53643c9dc8788ac8784e20e3b98a8ce736329e8bc9781e20e379988ca7868ca70719cff3643cd793f1bc4cc6733719cf20329e51643c33c878ba90f13c41c69324e3b9858ca71d19cffd643c43c978c691f13425e32923e379968ce732329e6bc978ee24e31948c633898ce711329ee9643cddc8782692f1cc21e3e945c6731319cfbd643c43c878c690f13c4dc6534ec6d38a8ca7988ce76a329edbc9780690f15c4ac6339c8c671a19cf04329e0a329ef6643c3790f1bc42c6534ac6730f19cf60329ed1643c9dc978ae20e379928ca70f19cfad643cf3c878fa93f10c23e3994ac6339e8ca70319cf73643cd791f1bc40c6731719cf43643c23c978ba92f13c4ec633978ca73719cfcd643cfdc878aac878a690f18ccd118f7ab75dde9d0c800ba724e49702cf3c073c8eea598adf35f82ec6f52aad961b5a2d30b42a86324b40bfe50ef42b00bfb26e995f0e3c727e24acf84d85174818c536cf314f02ea2c53a67d6039f0b8d8271dd53315ab2b8c3abd60d15dca60acae70504fdbbe23f32b80e7659d17d604947b9984516c4b1df324a0ce32658ad515c0e362df7154cf54acae34eaf4b245772983b1bad2413d6dfb8eccaf049e57745e581350ee151246b12d77cb539e803acb94295657028f8b7dc7513d53b1bacaa8d32b16dda50cc6ea2a07f5b4ed3b32bf0ab68367f6cc3666c523fd6d853501e52691308a6d85539ef2d204d459a64cedd82ae071d1ce3bd23dd58ead36ea34c9a2bb94c1585deda09eb67d47e6575b7c9704f16ab1a60e5aacb1f0acc9b116e22f5be6250d90d9ebec758e62f63a7b9da398bdce5ee72866afb3d7398ad9ebec758e62f63a7b9da398bdce5ee72866afb3d7398ad9ebec758e62f63a7b9da398bdce5ee72866afb3d7398ad9ebec758e62f63a7b9da398bdce5ee72866afb3d7398ad9ebec758e62f63a7b9da398bdce5ee72866afb3d7398a994167c523e3df096b02ca4d266114db4ab73ca9f7822607874f05c67c12f26b806795037d1cd533d5877cad51a7c916dda50cee5f6b1dd4d3b6efc8fc5ad80ed930af6e80cc5ee7fa312b9e293a2fac0928378584516cabdcf2a4dab129c1e153a6766c2df0b868e71dd533d58ead33ea34c5a2bb94c1fd6b9d837adaf61d995f07dbc1337b661bb3e299aaf3c29a8072534918c5b6c6294f59eafdc6a9c1e153a6766c1df0b868e71de99e6ac7d61b759a6ad15dca60acae77504fdbbe23f3eb613b64c3bcba01327b9dbdce51cc5e67af7314b3d7d9eb1cc5ec75f63a47317b9dbdce51cc5e67af7314b3d7d9eb1cc5ec75f63a47317b9dbdce51cc5ee7fcd159f14cd379614d40b969248c625beb94a763eab9c3b4e0f029d37387f5c0e3e2b98c23dd53cf1d3618759a66d15dcae0feb5c1413d6dfb8ecc6f80edd0d899573740661f1bb961f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e6286686d8503cd3755e5813506e3a09a3d8d6b9e5497df7607a70f894a9dfce06e059ef401f47f54cf5dbd968d469ba45772983fbd74607f5b4ed3b32bf11b68367f6cc3666c53343e7853501e56690308a6dbd5b9e543b3623387ccad48e6d041e17edbca37aa6dab14d469d665874973218ab9b1cd4d3b6efc8fc26d80e9ed933db9815cf4c9d17d604949b49c228b60d6e7952edd8cce0f029533bb609785cb4f38eea996ac7361b759a69d15dca60ac6e76504fdbbe23f39b613b7866cf6c63563cb3745e5813506e1609a3d8363ae649409d65cad48e6d061e17edbca37aa6dab12d469d665974973218ab5b1cd4d3b6efc8fc16e099adf3c29a8072b34918c5b6c9314f02ea2c53a658dd023c2ef61d47f54cc5ea07469d665b74973218ab1f38a8a76ddf91f90f80678ece0b6b02cacd216114db66c73c09a8b34c9962f503e071b1ef38aa672a56b71a759a63d15dca60ac6e75504fdbbe23f35b8167aece0b6b02cacd2561141bb663731df114193c45162d8e956fa5450f9d3f41ff26e0ff3d80d155db32d76094798c71b115e540b356064f2b43b363e95b6951017935e1f6aa004686edd52a079ab53178da189a1d4bdf4a8b9e3adf5affe2f6ea098c0cdbab0df038689fcb13068f9a321dbbb73ad6c7513d53c7ee6d815d773c0e49193c766f73504fdbb984cc6f83ede0993db38d59f1f4d579614d40b9be248c62c373feedf1f394270c1e35656ac7b63bd6c7513d53edd88ec0aefb76d05dca60acee7050cf02f02beb96f91db01db2615edd0099bdcef563563cfd745e581350ae1f09a3d8b601cfcef879ca13068f9a32b5633b1debe3a89ea9766c5760d77d27e82e6570ffdae5a09e05e057d62df3bb603b64c3bcba01327b9debc7ac78faebbcb026a05c7f1246b1ed009eddb1f3a4c77c411e35656ac7763bd6c74d3dd3edd89ec0aefb6ed05dcae0feb5c7413d0bc0afac5be6f7c076f0cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfcccdac7806e8bcb026a0dc001246b1ed029e0f63e7493f77401e35657aeef0a1637ddcd433fddc616f60d7fd43d05dca60acee7550cf02f02beb96f9bdb01d3cb367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed93373332b9e813a2fac0928379084516c7b80e7a3f879ca13068f9a323d77f8c8b13e8eea997aeeb02fb0ebfe11e82e653056f739a86701f89575cbfc3ed80efb3cb367b6302b9e413a2fac0928378884516c7b81677fec3ce9e7a7c8a3a64cedd87ec7fab8a967ba1d3b10d875df0fba4b198cd5030eea59007e65dd327f00b64336ccab1b20b3d7d9eb1cc5ec75f63a47317b9dbdce51cc5e67af7314b3d7d9eb1cc5ec75f63a47317b9dbdce51cc5e67af7314b3d7d9eb1cc5ec75ce1f9d154fa5ce0b6b02ca5592308a6d1ff07c1c3b4fc7d284c1a3a602633e09f98f1debe3a69ee9e70e0703bbee1f83ee5206f7af830eea59007e65dd327f10b64363675edd00997d6ce486d9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a398196243f154e9bcb026a05c1509a3d80e00cf27f1f394270c1e351518f349c87fe2581f47f54cf5db3914d875ff04749732b87f1d7250cf02f02beb96f943b01d3cb367b6312b9e613a2fac0928378c84516c0781e753073c0983474d99dab14f1debe3a89ea976ecb3c0aefba7a0bb94c158fdcc413d0bc0afac5be63f039ee13a2fac0928379c84516c8780c745ac2a9e228347e63f25f0adb4a8d6f913f42f6eaf6a6064d85e4539d0ac95c1d3cad0ec58fa565a8c81bc9a707b8d014686edd52a079ab53178da189a1d4bdf4a8bb13adf5affe2f61a0b8c0cdbab4d0e343b96ede1b1dcb78f659c7acd8f9de605c750f38263a87981d79c4a7307c797323c9605c0805312f29f01cfe7f1f3a4ee717d9605cfe7c0f3c3f8793a38aa67a95aef8f803daef52aad7e6c68f599a155319441861f3bd0af00fccaba655efc7966cf1cc58ce7b6c29a80729f92308a4dda2057ed86aafb157a5db2fe66613a7472ad5f17cf1ef0be6b33bd5ee1107f8550e6fe92dab29f6bb622f8bf6c37559f8386cdd1fbc01d6ccfc0645efc1505b9bb0f9ae9be2c6ae1e2d94db6c7fd83169eefe2e329c5fd1c7d1d7054f76c9ea31db0f0c458f70e51cf10f7c75ff754fbd15eaf4bd6aff6d12f4e76aa7939ee7bd27eb437ea5c08653a95d496fd06da0f5b5be17adf94737273df6c12d4b667c255a2ede6f395efb45dca7d02e5b1cde9a17f71ffec017575d52e46dd63c276d16cbb5d6a6f3ee3337d17832e9f906a667b4e813a5658b82b08b8311e73b99fc9ba6dcfc82a0c1dd934c36dfd8945c79e16ee9e04dc8cfb754f434736cd8eb65ff7b570f725e066dcaffb1a3ab26976b4fdba9f85bb1f0137e37eddcfd0914db3a3edd7fd2ddcfd09b819f7ebfe868e6c9a1d6dbf1e60e11e40c0cdb85f0f307464d3ec68fbf5400bf740026ec6fd7aa0a1239b6647dbaf0759b807117033eed78382c37564d3ec68fb75a585bb92809b71bfae347464d3ec68fb759585bb8a809b71bfae327464d3ec68fbf5300bf730026ec6fd7a98a1239b6647dbaf875bb887137033eed775edb7cfba5f575bb8ab09b819f7eb6a434736cd8eb65f8fb1708f21e066dcafc7183ab26976b4fd7aac857b2c0137e37e3dd6d0914d33db7eede8bdbcb26cdf133ce4549ff478cd87b2e0f918785cc494a3382875d4cf25d53775bfa1d521432b1c07e300e8e7a02f4cc6f7fbc59f67f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b667e66fcc6213e5f91729f90308a0d9f49b9b8cfafea7ea55e97acbf5998ba9d5aebf740ec7ecb4a0b0c7f49e0107f8550a6c979b5657b6ab6a2e0c8ed86e35ae3b6dc177b1dd2dbd28c7f99177f45509ffdc0e3e0fdfc14cf0183e780450b7cef341edf6543dc685c56aabebfd332a8ddcefb8cfaa0a61fc5eeff704d0b0c4d3f72ec3b111cbe3d8501a724e491c7c5b36147f54cb5057b8d3a991a1743998ba19e7b1dd4b300fccaba657e2ff0c884cfe45dc56060f004167d646a42c65345c6d38f8ce742329e9bc9784e27e3e94dc6f338194f57329e96643c23c9781e22e3b98b8ce73c329eebc87872710e9e0dcf73643c1dc8784e22e3194fc65348c6338c8ce720194f7f329e1f90f1dc4ac6733919cf99643c7dc8789e24e3e94cc67305194f828c673419cf60329e7bc8784ac978da92f1dc40c6730a194f05194f7b329e09643ccdc9788693f15c4ac633808ce712329edbc978ce26e3b99a8ce769329e72329e62329e56643c63c8788690f1dc4bc6730119cf4d643ca791f1f422e39948c6d38d8ca70519cf23643c03c978ee24e339978ce75a329ecbc8789e25e32923e339918c671c194f53329ea1643cf793f1b423e3b9888ce716329e33c87892643c4f90f17421e3399e8c671419cf20329ebbc9784ac878ae27e3799e8ca70719cfc9643c35643ccdc8781e26e379808ce762329edbc878ce22e3b98a8ce729329e4e643c2790f11491f15493f15492f1f425e3399f8ce746329e53c9787a92f13c46c6d39d8ce738329e11643c0f92f1dc41c6730e19cf35643c5792f1b426e36943c6f30c194f47329e02029e4470e4b79812f0ff0360936f067d0cb62696f5c9735829af8e8b53db1eb9ee2696757f6461409d3e84ba2475bef4fb4d299dd05712e6c55f11707c44c2d3918ce719329e36643cadc978ae24e3b9868ce71c329e3bc8781e24e31941c6731c194f77329ec7c8787a92f19c4ac6732319cff9643c7dc9782ac978aac9788ac8784e20e3e944c6f31419cf55643c6791f1dc46c6733119cf03643c0f93f13423e3a921e339998ca70719cff3643cd793f19490f1dc4dc633888c671419cff1643c5dc8789e20e34992f19c41c6730b19cf45643cedc878ee27e3194ac6d3948c671c19cf89643c65643ccf92f15c46c6732d19cfb9643c7792f10c24e379848ca705194f37329e89643cbdc8784e23e3b9898ce702329e7bc9788690f18c21e36945c6534cc6534ec6f33419cfd5643c6793f1dc4ec6730919cf00329e4bc9788693f13427e39940c6d39e8ca7828ce714329e1bc878da92f19492f1dc43c633988c6734194f828ce70a329ece643c4f92f1f421e339938ce772329e5bc9787e40c6d39f8ce72019cf30329e42329ef1643c2791f17420e3798e8c673f19cf75643ce791f1dc45c6f31019cf48329e96643c5dc9781e27e3e94dc6733a19cfcd643c1792f1f423e3a922e36942c633d6e0c1ffab77c3e4fc48be1d5408ff1fa83b97b7d6eb9232f28e92bad7b5c7b0a9faee7654df3d41ed9484f9dd505f61df033c7b1cf17c68f098be8b205f019aed326c8a71a723c65d06a3ccef0446d16f17f0ec72c4b3dbe0317d1741be2768b6c3b029c6ed8e1877188c32bf1d1845bf1dc0b3c311cf4e83c7f45d04f9bea0d936c3a618b73a62dc6630cafc566014fdb601cf36473cdb0d1ed37711e4fb81661f1836c5b8c511e30706a3cc6f0146d1ef03e0f9c011cf5683c7f45d04f9fea0d966c3a618373962dc6c30cafc266014fd3603cf66473c5b0c1ed37711e40780661b0d9b62dce08871a3c128f31b8051f4db083c1b1df16c32784cdf45901f089aad376c8a719d23c6f506a3ccaf0346d16f3df0ac77c4b3c1e0317d17417e1068b6d6b029c6358e18d71a8c32bf061845bfb5c0b3d611cf3a83c7f45d04f94ad06cb561538cab1c31ae3618657e15308a7eab8167b5239e35068fe9bb08f255a0d94ac3a6185738625c6930cafc0a6014fd5602cf4a473cab0c1ed37711e4878166cb0d9b625ce68871b9c128f3cb8051f45b0e3ccb1df1ac30784cdf45901f0e9a2d356c8a718923c6a506a3cc2f0146d16f29f02c75c4b3cce0317d1741be1a345b6cd814e322478c8b0d46995f048ca2df62e059ec886789c163fa2e82fc18d06ca161538c0b1c312e3418657e01308a7e0b8167a1239e45068fe9bb08f26341b3f9864d31ce73c438df6094f979c028facd079ef98e7816183ca6ef22c8df0d36e1ed0eb6f775be1bd8ded3f9ae607b57e7bb80ed1d9def0cb6b775be13d8ded2f972b0bda9f31dc1f686ce9781ed759def00b6d774be17d85ed5f9de609babf349b0cdd1f93e609badf357816d96ce5f0db6993a7f0dd866e8fcb5609baef3d7816d9ace5f0fb6a93a7f03d8a6e8fc8d609bacf337816d92cedf0cb65774fe16b0bdacf3b782ed259dbf0d6c2feafced607b41e7ef00dba33a7f27d8eed3f9bbc07648e7ef01dba73a7f2fd83ed3f9fbc1f6439d7f006c9febfc8360fb91ce3f04b61febfc60b0fd44e78780eda73a3f146c3fd3f987c1f6739d1f01b65fe8fc2360fba5ce8f04dbaf747e14d87eadf3a3c1f61b9d1f07b6dfeafc78b0fd4ee76bc0f67b9d9f00b63fe8fc6360fba3ce4f04db9f74fe71b0fd59e79f00db5f74fe49b07da1f34f81ed4b9d7f1a6c5fe9fc3360fb5ae79f05db373aff1cd8feaaf3cf83ed6f3a8f63ddfe5de74b8278dbd96f83daa9047c8b3f55e61f3adfdc2823cb16429913750747f58c43bd8b27edb0b4cbca26edf0fb609376f83db0493bfc2ed8a41d7e076cd20ebf0d366987df029bb4c36f824ddae137c026edf0eb609376f835b0493bfc2ad8923a3f176cd20ecf019bb4c3b3c126edf02cb0493b3c136cd20ecf009bb4c3d3c126edf034b0493b3c156cd20e4f019bb4c393c126edf024b0493bfc0ad8a41d7e196cd20ebf043669875f049bb4c32f804ddae147c126edf07d6093fde55bb049db7c086cd2367f0a36699b3f039bb4cd3f049bb4cd9f834ddae61f814ddae61f834ddae69f804ddae69f824ddae69f814ddae69f834ddae65f804ddae65f824ddae65f816db4ceff1a6cd236ff066cd236ff166cd236ff0e6cd236ff1e6cd236ff016cd236ff116cd236ff096cd236ff196cd236ff056cd2367f0136699bbf049bb4cd5f814ddae6afc1266df337607b5ee7a5ad6e01367956aca6d2ef39e1383c78ce2f4cc920deb61fa724e49f83bacbd4848ce755329e2a329e7e643c1792f19c4ec6731f194f4b329ee9643c6f91f1fc958c673519cf2a329e6d643c5bc978ce23e3d94fc67312194f2119cf1c329e61643cef93f11c24e3e94fc6f303329ecbc978ce24e33944c67305194f828ce70d329ea9643c2bc9785690f17c40c6b3858ca7948ca72d19cf29643c15643cedc9789a93f1cc22e3194ec6f32e19cfa5643c03c8782e21e3399b8ca7988ca71519cf64329e31643caf91f12c27e35946c6b3998c671319cf05643c7bc9783e24e3398d8ca70519cf0c329eb7c9780692f19c4bc6339f8ce732329e13c9789a92f1cc25e3594ac6b3848c671e19cf46329e0d643cedc8782e22e3d943c6b39b8ce70c329ee3c978a691f1bc49c633888ca7848ca70719cfc9643ccdc8786693f12c26e35944c6f31e19cf7a329e75643cdf92f1ec22e3d949c6731619cf09643c45643c53c878aac9785e27e3a924e3e94bc6733e19cfa9643c3dc9788e23e39949c6b3908c670119cf3b643c6bc978d690f1ec20e3d94ec6730e19cf95643cadc978da90f14c22e32920e04900470036f97f53b07da3f307c126dfebd90fb6af757e3ed8bed2f9e7c1f68cc5d6c4c2270cdf804dde6d7e166c72fff36bb0c93b015f814dceabc4bf9a9fdbf648fe26b08cf8696ae1477f5f59b8248fdb5b964906f16e6ff4950c8efc9e5211703c4bc233898ca70d194f6b329e2bc978ce21e3d94ec6b3838c670d19cf5a329e77c8781690f12c24e39949c6731c194f4f329e53c978ce27e3e94bc65349c6f33a194f3519cf14329e22329e13c878ce22e3d949c6b38b8ce75b329e75643cebc978de23e35944c6b3988c6736194f33329e93c9787a90f19490f10c22e379938c671a19cff1643c6790f1ec26e3d943c67311194f3b329e0d643c1bc978e691f12c21e3594ac633978ca72919cf89643c9791f1cc27e339978c672019cfdb643c33c8785a90f19c46c6f32119cf5e329e0bc8783691f16c26e35946c6b39c8ce735329e31643c93c9785a91f11493f19c4dc6730919cf00329e4bc978de25e3194ec6338b8ca739194f7b329e0a329e53c878da92f19492f16c21e3f9808c670519cf4a329ea9643c6f90f124c878ae20e33944c6732619cfe5643c3f20e3e94fc673908ce77d329e61643c73c8780ac9784e22e3d94fc6731e19cf56329e6d643cabc8785693f1fc958ce72d329ee9643c2dc978ee23e3399d8ce742329e7e643c55643caf92f13421e3196be199ef8847faaec8ba657e7e23f7bddbf0bd3b4f7cef347cefcc13dfdb0ddfdbf3c4f756c3f7d63cf1bdc5f0bd254f7c6f327c6fca13df1b0cdf1bf2c4f73ac3f7ba3cf1bdc6f0bd264f7caf327cafca13df2b0cdf2bf2c4f732c3f7b23cf1bdc4f0bd244f7c2f327c2fca13df0b0cdf0bf2c437f3f5b7eaa72cdf82d8ab7f13f07f1c3f6ebf23c6f906a3ccef0746b1e1f8ef3d1cf1445dbbf720f0adb490be8bf20e7d02fe5f018cae62aa87c128f3b698c2f1462b1cf144dd73a820f0adb4906f39ca37b112f07f1c3fc5554c55188c326f8b291cffaba7239ea87b253d097c2b2de45b8ef20de004fc1fc7277215533d0d4699b7c5148e37d1d7114fd43d9ebe04be9516f22c469e9927e0fffd80d1554cf5351865de16535b81a79f239ea87b53fd087c2b2da4ef99f4594ec0fffb03a3ab98ea6730cabc2da6b6004f7f473c51f7d4fa13f8565ac8bb51f20e6b02fe3f00185dc5547f8351e66d31b509780638e289ba173880c0b7d262a0cecb377212f0ff81c0e82aa606188c326f8ba90dc033d0114fd43dcc8104be951683745ebec19980ff0f0246573135d06094795b4cad039e418e78a2eebd0e22f0adb4a8d47919032101ffaf0446573135c86094795b4ce1786d958e78a2ee195712f8565a48df18e9339880ff5701e320478c9506a3cc0f0246b1ad029e2a473c51f7baab087c2b2da4affb4afd9b80ff0f03465731556530cabc2da65600cf30473c51f7e88711f8565a0cd779f9864702fe3f1c185dc5d4308351e66d31b50c78863be2897ab6309cc0b7d242c63e906ff225e0ff385eb6ab981a6e30cabc2da696004fb5239e4506cf228b16c7cab7d242befdb258ff26e0ff6380d1554c551b8c326f8ba945c033c6114fd4b39c3104be9516d2b773a1fe4dc0ffc702a3ab981a6330cabc2da670fce5b18e78a29e418dcd81efa8e729b9f01df56c2017bea3ee73e7c277d43ddb5cf88ebaff980bdf51f7d272e13beabe502e7c47dde3c885efa8ebf55cf88ebaf6cc85efa8eba85cf88eba26c885efa8f3db5cf88e3a57cb85efa8f30edf9efbf63c6edfc7f2dc215fdbf363790c3d96c7127f6de0af0d72e5db1f4bfcb541ae7ce7ebb5816fcf73df9ecbf55741107d3db6cc91ef25866f99c7e72c4b1cf95e64f896797c66b0c891ef05866f99c7fbdf0b1cf92e327ccbfc821cf86e65f86e9543df6d0cdf6d2cbe5d6cef4470f8f5b730e094843cc6c042073c8eea59aad6bb58afebbb18d76bbb6f63ee2fc5506631e8e7baed9075676a3b5ac4e7bb34013e64cc38659367b1ef834ddad0f7c026cfd8df059bb4f3ef804d9eefbc0d3679fef316d886ebfc21b0c97358ecff2ecfd2b783ad52e7b1dff5209ddf0a36e99784fd7da56fd916b049ff40ec672a7d3c37814dfae962ff46e96bbd016cd25f1efbd5c93b0febc026efad607faefd3abf066cf28d4aec47f4b5ceaf02db373abf126ccfe9fc0ab03dadf3f781ed4b9dff166c4fe9fc02b07da1f30bc1f6a4ce2f06db5f74fe4db0fd59e7df00db133aff3ad81ed7797c2fec4f3aff21d826ea3cbe8ff4479ddf0db6c7741edf83f983ceef04dbef75fe35b04dd0f957c156a3f373c1f63b9d9f03b6f13a3f1b6cbfd5f959601ba7f333c1f61b9d9f01b65febfc74b08dd6f96960fb95ce4f05db289d9f02b6913a3f196cbfd4f949607b44e7ff0ab65fe8fc22b035d1f9256093f105b1cf877c337319d864dc6ceccb2363098c05db713a3f066c2d74be1a6cf29db5e16093b17a87812da1f355603b41e72bc126e73a83c02663ad0c049b9c970c005b6b9def0f363987e8073619fbb02fd8e47b9e3dc126637a57804dbee3df036ca7eafc7cb0c9f866fbc126df5c3b08361947f86bb0c9b796bf01db593aff1cd864cc97a7c1768ece7f09b67375fe29b0c9f739bf005b89ce3f09b6b63aff17b09daff37f069b8c0ff604d8e49b6e8f834dc6e1fd13d8e4dbc913c176b1ceff116c97e8fc6360937154fe0036194bf2f7606ba7f313c026df90ae01db153aff3bb0c9d816e3c176a5ceff166c3286c138b075d0f9df80ad4ce77f0db68e3a3f1a6ce53aff2bb075d2f95160ebacf323c1d645e77f09b6ae3aff08d8bae9bcb4336a7f56fbf9013d9f0ce23dcffe38387cca749e2d0cc813e7796b31f0a0af7db1d7bd2c758e2c6d4113bd5e89a17de07b6fecbed3e7e71fe97515eaf5ee357c174299d6ba7150cbc931bfa95e6ebfb11cde139275cb325782fd4363ddad757d3f7254dfbd069370a30e52e664cda48e8ddb75ded1fbf0656a1f90580b40439c92901706375a9595e2b9705d783e029e7db1f3a4af7d5dc404ee5b715ffb9af744cd582b86327b41bf0f1de887fbbaac5be6c59f67f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b667e66c57340e7f159b3943b40c228b67dc0e3e23e3f3e8795f5abe73acbceabf5bb2f76bf873fdf6ba6d75b6ad4b910cafc1d9e39add2791cc753b65bd4b674f09c30e3b6147f45501f7c1674c011cf3e83679f450bc997c4e6bb6c881b8dcb4a55bf15f58c7dbfa1eb018ba6aef6d77d7a5d0586a6b8bf7e6cf0e0b3d122e0fd44ff26603d9f401d1cece319e342fce1beb40f6c92ff18185d6c673c96487b20cfc3f1d9b494d96f3c178f7fdb9795ba6c37f6409d92c191f15d08650e41dbf799ce63df9003a0dbef2cff9729d3736ad14fd57957fc754e6ddf9dc099043fe87b07b0c6e4fbb0f7510a74123f622f84fc6fa13f8794133d446b6157fb88f4e9447673b98f8de58aa1cc6e4bfd9341bcf5df65f0ec3298d536f911c4d9efe0f8efaa4dda1da1d195a09194d9071aed77c4b3cfe0110ef1a7cac8f66f6e9491650ba1cc97d046a9ba483b2ff5c4be2d780c70751c135fb26e99b79d1b7f048c661d557c743bb596775fecbc0dabffd7bf417b1177ffaf7f83182a008ec0587f29ac5fb89a07d1c71629f33f8de3a88b6b0cdc96a69ea8b394f95fd00eb52f49e7b339d73f56d76d51e7fabb1df02482c3afbdd594e9f88ec7983d0e781cd5b3d476ecfad0a8533194b918eae9e03c26e37bb5bbc0b78b6d8e5ac839d45e438b4228d3b224fd2b6d47948e78adfa514eea52663d1f2cb5d445cab42aa9ad4b0bb0c7c9e472bbed803aa9f5eeb3d455ca9c5c52abcba93a9f80ed84f74d2eb2fc5fa64ced018e6bb32dfe3aa7b6ef56e04c821ff4fd01b0c6e4fbb0ef6ac8f9bef8117b21e42f2ca92d2be5440fd15ad8d53e22effc21bbb9dc5e63b96228b3dd52ff64106ffdb7193cdb0c66b54dce28a9cd4b1cb96c37b74768540a1a4919bc7f2cc7767cefce76dcdfe7883beab8bf0f18cd7613cf5d5cb2ed37d8cc7ba8b6f3412983e76452a66349fa57b5b3094b59f3deb08bfb98f86e6900f5088cbaca8431e0e0dab01caf9da49d123fedc1be4fe745e7f686768550a66749fad7e179b7f5dea5797d87d714c26dee5bf8eec75525b5dc2aee653bedd3bf4560fb54ffb6089c5ca795dbee190a87ed9ee10d25b5ecb8ac701db2d4c5bc466e121c794ffd3ba32cde77cbb41cfa52d3416319a5efa716a67d869f16b06c6cef6a74282d8de23f087c1f1bcc365d653fc098dba7f3e6be82d7c652e6be92f4afb4496659b5edbf38b9561fd98ea21db62718931f036352e74bbfdf94dae70e1af59779f1a718e57ecf41e089bfed4abfaf94cd7de0fdc0e3a26d77d44697e231b6456cebed56693bfe7f6c6895c3e7b5d663bef9ccbd85918fc7775995edfe934d8bbd161e57cf51a2b4d86bf11d9f169d87d8da399b16b9ecfb10a5c58716df316a310cef7b66d2628f85c7c5bda84c5aecb1f88e4f8b2ea5999e6ba016bb2d3caeee3d446921feb265fe9080b985918fc77779a5ed3e994d8b5d161e57d7cd515aecb2f88e4f8b0e9df11e5d262d765a78e2bf3f97598b9d16dff169d1b51bdec3cba4c50e0b8fab67ba515aecb0f88e312e86daeee5d8b4d86ee1d99e632db65b7cc7787ed8d976afcda6c5360b8f83fbae19b5d866f11da31683f1be6b262db65a78b6e6588bad16dff16951d9c9764fd8a6c507161e57f784a3b4f8c0e23b3e2d067755beb7d4418b2d169e2d39d6628bc5778cd750a9b8d85c072d365b7836e7588bcd16dff16951953ad7da54072d36597836e5588b4d16dff169519a3aa66eac83161b2d3c1b73acc5468bef18e322753db9a10e5a6cb0f06cc8b1161b2cbe633c8ea4e2627d1db4586fe1599f632dd65b7cc7a7c5b0d4fda77575d0629d85675d8eb55867f11de33d97545cacad83166b2d3c6b73acc55a8beff8b4e8983aa6aea983166b2c3c6b72acc51a8beff8b4189a7a26b6ba0e5aacb6f0acceb116ab2dbe633cef4cb517abeaa0c52a0bcfaa1c6bb1cae23bc6f3ced4fd8b9575d062a58567658eb55869f11d63db993aef5c51072d56587856e4588b1516df319e77a6b4585e072d965b7896e7588be516df319e77a68e23cbeaa0c5320b8fabf144a2b45866f11d635ca4dacea575d062a98567698eb5586af11de37dad54dbb9a40e5a2cb1f0b81aaf214a8b2516df315e8fa4eef12dae83168b2d3c8b73acc5628bef189f15a5cec117d5418b45169e4539d66211f8de1fbbef747f6ef1217db1ae30b4288432cddba67fa52f56948eb20eec57867559187b5dd2fdca1644d46521d445ca9c007569113819efa7dc515d5331331feaa4d6fb89a5ae52a64ddb5a5d4ed2f9046c9343a0dbf996ffcb5460cc27212ffaa93abf1f7f9d53b1fa1e7026c10ffa7e175863f2dd017d17e8247ec45e08f9b66d6bcb4a39d143b41676b58fccd3796437975b642c570c65e659ea9f0ce2adfffb06cffb0673eabd0788338923376d579a695e84465780465206fbec7de288c7ec43281ce24f9591eddfdc2823cb164299cba08dc27ea552cf447064bf49476d5907649775cbbcf82b06db7e6034eba8e2e310f4fd94b122641c09659371213ac27aba183655d7ae8eea2abe64dd32df1518659c8a2eb9672cab2b63678351f17477a0198ebd2153a6e34577e0e9e680c7513d53c7a11e469dba1a752a8632f86e630f07f52c00bfb26e99ef01be5d6c73d4428ec9971a5a1442991b8cf3c7281d651d2a7ebb58ead2cb715d64ddd22ef5ca81ef0ac37727c37722387c3b0741e6fdab02987b3a6056ebed1dff7a4bf1bc4d624afc74823af5010de2aa13ae4bcef3fa18da1642fe4138cf937252568e5fc2ae6259b625b29bcb7537962b8632bd2cf54f06f1d6bfb7c1d3db6056dbe42e38b773b03fa462a097c121f39d40bbde11daf502eda40c1effba38d2aea7c1d3d3f0ad78e41ca707d8e45c41f813f0ff8e39e036dbbd1e166eb1e13871b6739dcef133663cd7e90c8c62eb093c158e3433b7f5a5863e785c6e6e9491650ba1cc383836262c65d57e7771416dbd9a6a7b6cef8ee936bdb903bd709cc600f4090c0d03d04bead9cc01cff141ed588de36baac70d7e78e85d43d38f1e05add0c0c4df024b359a800df34d2db620387c48ca42b0c99094cdc0d6c4900587c294f232a49d0bb9500f5977a1c1d90258e2f48dc379ca9429748e031e17a1ac424786f4d4a173dfb8113543313e9a199cf5891df5bfa619ca45adcbd57630f78924cc9b3158e8c87f53a86f12e6c59fda3632b4ea98c1431eed33eee109a3868eae198f42993b36e60b82c33780f91b25b8ab9d0e03002b8c8d4333a35ed860c8ff64c31c1f3f67398e996b6a13803f998e07dd5a3ad04dad5fc6be1d3278e4c83b26548e1c31e4ba09a387d48ca81e8d5bb385a15cd49696ff37079bad89c7b26ac2660b973dce62b34d38ca700bb0c991ab25d884e778b03585bc9437b78c9370bd10d62fbb94fa9f12a799aef871416d08c8e158b5ab6aff55a772ea13b2ea54480d6dac36a71aba58dd31544313abafd8a9a187d550c36a68e13383f4d0c16aa8e07382f450c0ea6b1725417a68dff383daa17bd57408382f0ad243f3aadb349704e9d32e35b46ebb203d74aeba75d93e487fe64dbd5baf4edfd56d0175caab2ef1d4e5883a0555a79eeaf685ba95a54ee9d4e9b23a1554a76fea72a4b7d6ba4f98ae0ad3d561ba264cd786e9ba305d1fa61bc27463986e0ad3cd61ba254cb786e9b630dd1ea63bc2746798ee0ad3dd61ba27480fef7c6f901e7e5d0dff7c7f901e1afa81203d6cf483417a48e98782f470d38383f450d44382f430d54383f410d60f07e9e1ad4704e9a1731f0dd243edaa21784707e9e1b0d530d96af86c35f4af1a26580d29ac861a564315ab618dd510c86a68e42783f430cc6ac8e667c2f46c901ed2f9f930bd10a617c3f452985e0ed32b417a7870356cf894203dccb81a7e7c7a901eae7c66901ede5c0d7bae864357c3a4abe1d3d5b0ea6a987735fcbb1a16fead30bd1da67782f42309f528463da250b7ffd56330758b7a6190be75be38483fe2568ffc551708d5254475915919a4bb50a92e65aa8b9dea72a8ba60aa2ea9aa8baeeab2acba70ab2eedaa8bbf7ae541bd02a25e8951af08a957a6d42b64ea953af58aa17a4d54bd76a95e2356af55ef0bd2b7c50f04e947a5ea76b87a34a0e251ddbeff2c4c3f0cd3e761fa51987e1ca69f84e9a761fa59987e1ea4873056c31dabe192d5d0ca6ac865353cb31aca590d05ad8688fe63901e7a5a0d5dfd97203dfcf59761fa2a480fc1fd4d901e7efd6f61fa364c7f0fd33fc2f4cf30fd2ba81d4a1b1b8b33750ba3af5282c1353543478da929a9a92e19356164cd8831239f289938a2667849f56343c70d1b593d1117fe5c2f2ce380f719376ef013252346570d7dbca47a424d49f5b092caea09a3ab0e3b50ff532f74f6911e075755453bfbf7ef43fa7feae9b4a56efb6484f51b33d7ed84a6f510e4c4fa2cd4b969fd2af4a43e4ac9e5ecdde973dd92f123ab6b4a4a4b46877fc3836bf5c4a155ed4bf07fe34391c7d7948caf193caea664d8b8ea51251ddae37a271d5f8f4a1424dcc0b439b37ee2b4d2df52aa5788ed3baf1e0a7c715efd48ffdbf721fd1ff574daa2a41e352caecf426525f523ac28899465fc84ca9a718387d4442fdce7fb2c7c7d7daa796f3dabd9ac6d3d9c25eab3d0a56deb47787d7d9c8dcdc259f0ff015dc8f1201c4f0600", + "bytecode": "0x1f8b08000000000000ffed9d09941545bee6b38a62d16b95b82bb8948a82acb72ec58e50b8e0ae08222020140508c84eb183b28328822cb243b1ef8be0d26def76b7bda96d2f6adbfdba5f77bff7fabd7ef3ce9cf3e6cc3b33f366c69e8c7be33ff511449675cb8cf2bb12794ed48dfc2a32e3175f46462e9199911764a6bf86214fc71b84a14570fe24ff2fd3bfc92f3695c4b8aea44bcebc1ce1cccf11ce0639c25990239c0d7384b3518e7036ce11ce2639c279518e705e9c239c891ce1bc2447380b7384b32847382fcd11cea639c279598e705e9e239c57e408e79539c279558e705e9d239cd7e408e7b539c2795d8e7036cb11cee639c2797d8e70de90239c37e608e74d39c2599c239c37e708e72d39c2796b8c9c6d8053eed9dfa67f6fd7bf2df56f2bfd7b87fe6dad7fdbe83216e8f9b661681786f661e860fc4f19a36edca7c2d0d1f85f69183a85a17318bae8ff15ebff750d43b730740f438f30f40cc39d61e81586deda8f3e61b82b0c7787e19e30dc1b86be61b82f0cf787e181303c188687c2f070181e09c3a361782c0cfdc2f07818fa876140189e08c3c0303c19864106cbe0300c09c35361181a866161181e86a7c330220c23c3501e865161a808c3e8308c09c3d8303c13867161181f86096178360c13c330290c93c330250c53c3302d0cd3c330230c95619819865961986d7836270c73c3302f0cf30dce0561581886e7c2f07c1816856171189684616918968561791856846165185685e18530ac0ec38b6178290c6bc2f07218d686615d185e09c3fa306c08c3c6306c0ac3ab61d81c862d61d81a866d61d8ae596447d811869d61d81586dd61a80ac39e30ec0dc3be30ec0fc381301c0cc3a1301c0ec391301c0dc3b1301c0fc389309c0cc3a9309c0ec36b61381386b361783d0c6f84e1cd30bc1586af85e1eb61783b0cdf08c337c3f0ad307c3b0cdf09c377c3f0bd30bc1386ef87e10761f86118de0dc38fc2f0e330fc240c3f0dc3cf0ccfdf0bc3fb61f8200c3fd7da87faf717faf797faf757faf7d7faf723fdfbb1fefd44fffe46ff7eaa7f7fab7f7fa77fff4efffe5efffe41fffebdfefda3fefd93fefdb3fefd07fdfb8ffaf79ff4ef5ff4ef3febdf7fd1bfaa1f6f75f34cbc49503d950531b539a563cad57d7c31d3ec27547d500df4ffe4b758eb057a5e7ef3b4de50cf3734f4467abe91b19e267abe89a117e9f922436faae79b1afae57afe7243bf52cf5f69e837ebf99b414fe8ff650a96f9515a032de58126f52f1fb4865a6b005a23591d688db5d61034d9be8d40bb486b8d41bb586b4d404b68ed22d02ed1dac5a0156a2d015a91d62e01ed52ad1582d6546b45a05da6b54b41bb5c6b4d41bb426b978176a5d62e07ed2aad5d01dad55abb12b46bb4761568d76aed6ad0aed3da35a035d3dab5a0e95d2cb80eb4ebb5d60cb41bb4d61cb41bb5763d683769ed06d08ab5762368376bed26d06ed15a3168b76aed66d05a68ed16d0e45ce156ada97a266d5f7a19ade783769becdfa0dd2efb36682d65bf06ad95ecd3a0dd01798bd65af671d0da684deab8fa5f671d2f0be26adf52156abd5de25e6fb866b5de6ef1af37a9dacbee41b5d765904f17f0aa878ec7f83c4309e69da783e4237a01c4ef83b4924efc90764ad855dbd355c77bd4b05c6763b92248d3d552feb220def2773378ba19cc6a9bf4048ef8eb6cc7a4afb3b59eb2aeb30321ad59f7e498f955acb30f0287833a5beaeb6cada7aceb6c05a435eb9e9cbf7d15ebec60e0705067bbbaa9b3a9a4afb3997b264160af7b722df155acb3638123fe3adbc9d7d9da4f59d7d9e721ad59f7e4baf6ab58672b8123fe3adba5ab3f37a8f594759d5d0369cdba27f758be8a7576097038a8b315be9dadf594759ddd0e69cdba27f7fbbe8a75761d70c45f67bb39aab31d7d9d0d32fd5f4160af7b72eff9ab5867770147fc75b6c2df9fadfd94759d7d0bd29a754ffa41be8a75f6b88eab7e860f753fc3f5a0fd426b3780f64baddd08daafb4761368bf86fe42d13e92be44d03ed6da2da07da2b55b41fb8dd1f7aab44fb5761b68bfd5daeda0fd4e6b2d41fb3badb502edf75abb03b43f68ad35687fafb536a0fd516b6d41fb93d6da81f667adb507ed1fb4d601b47fd45a12b47fd25a09687fd15a0ab47fd65a47d0fe456ba5a0fd556b9db4a6fa29a5efeb5dad3501aeb220be3a9a000f64ca33e6cb20dec12d4fb2087830af92f8f3eaa8ca7ecec3569f53f612e04939287b02f2a80d4f0a783ac6cf937ec7af34fef5a6b771d2f034017925a15c9d1c942b0ff29275cbbce457041aeed39d2c8cb19f6784175e799097ac5be63b03a368d8c6489b2bfb8f3ac6fc2bf03ad897d2e719929fbcbb2c1c25a04b9ae2e6d56cff556b85f07f6c6f3b1a9aa37a99ae179297ac5be64b8151cad3b1fe1953b5654c198caeda883cc84bd6edf3aede0e126f0f3c5d1df1986d9ae4ddb51ef2ee6ce45d62e48d6da74c351ddb3a03b383ebad922fe37a0bcf1f18aeb72ecbab4e6b5e37493b9cedf556276339c6ebad4679d51c0ef687741de86a70c87c0978d72dc2bbaee09da4b90dbc7370ee99f6ae8bc123f329e09176ac1478dad7334f7b82bcf11c56b61b5e4be17980abedd5de609479dbf6ea0c8c290ba38373c2544dc7c81260140def47251d7916b55d932479bbb8aec47729e4dcdcbcb6298034ff273ff3abdaca76794ed95278ee2c536daf79e3df4ea924ee1fb5e171bc0f9538aa8f49bc7ff359106f5d33db25b3bd89bac7e3aa2d4f1a3c322ff97966cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367e667563cd29f80cf6d49ba8e248ce6f36faeeef3a7bf8522ebd2bfaa5fe7dbd0af137fbf452a897d7ff21c444ba3cc0590e6c3bceab4efe8b87ae640faa9f0592bf3f92b977d76f83c6619cc4b7ef8ac156e4b86e79d8a63cb3bf35d1a17fd6d9706996fea98cf7e76b478eae2f967f434cff0149f03bfc3e051f5f46ff9d56c2efafeb2ed8b44af241e67df5e5160afebf16f97d4397dd6f9c1b9ed071e675c3dbf236db5f4977735f22e80347f80e7a8e4d92a196728692c87cffdc8ba659996a07733d6dd542f2b1c0d8df597c2b292e64fd0a62e83baeaa0ad4c65fbec3af69bc77f1ccef4e327b3e0690f3c2eda1947e71b49dc07e2eec7379f4fb39dc7481a7cb6cfc17395353eef24f97966cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367e667563c665f2bbe5f5b42c2289ae3671fd2fd19adf4ba64fdaa5f677a7e75beaefbe1a4cfa99551667c77f46c7e75da593a5e189cffbc43d4b674f57d8aa86d29f9e1b767da038f8b7edd3cc84bd6ddc1e285c48b63cb3bd38f1fbfc7d5fdf849c3d7128ba7aef657ec63454f717f4d193cd8371af56d9f0e86e6681fafb15e487eb82f75004de2f87eb48bed8cc712f3b91ec90ffbaf5fd6de360d5c6dfb54d265bb81df4d290bceafdff83d950dd0f66dd2717c86039f1dd96ff9bf4c35f5538b7faacc3de32f737afbde099c65900fe6dd0b5863cabb04f3ced341f211bd00e2fbf2abd34a3af143bc1676b58fc8376090dd5c2e652c5704697a58ca5f16c45bfe9e064f4f83596d932d50cff6c3f1df559bd423c2a396e091a4c1f32057cfe4996da4f97c233eb7d7c84883e72c92e638b451aa2cb6e7476dcf1cba3a8e453d73683b376e0f8c6619cde73c2ff4e7b4be09ed45dccf697d13ea103ea71518eb6f05eb17ae4641f4b145d27cd758bf794e2ecbe0736092e6fbd05efc471dcec9bfaceb2bdb39392e175576c58cdf412b8b9119eb04b2e07982a479dfd866dd22b83b5b96fd306259f1cafc56586170be7f6ebeb396d9e77b1a65917add03ca22693e36ce01e33f6fc99c03bafaa61c9e7ba8f576b09455d2fc0ef6b5dfc3399e6c27dc6fffcdf27f996a3a07c4f11c7bc75fe624ee33b27d7b5bf2ee03ac31e55d82eb9273c03e86070510ff2fd0a64b3a492b5e0bbbda47e43c0ad9cde5ba1acb15419a5e96f29705f196bfb7c1d3db6056dbe48f50cffe0dce015db5d5bd223c6a051e491abca768fb0ea8ed18e3ead9fea8634c076034db4d3c4eba6433df1332efabd9ce11248d2c8be708ff09ed6cc292d6bc5f28c7cb389f1bc677254a21dfbf59f22d8bd9cf2ee05b19cce379c19799b783ef84a7df11c1ef0a0a034e6510c76f87cb72f9c0e8e27beb89e0dcef037f1e237e835d966b008c2eb661b6df272e054659ae00185dbcff85dfc8af0d634760c47d51185d7c97b4aedf73c4ebee46c0c8f45e15de3f6e0c8c2eee73d4f57d1a3ce636815f57638724b360c463ae2c771130bab87f85f7ef6bc388e72eb2dcc5c0e8e23e73b663b0e0f7a1f1fe8f30ba3807ccf61a05ef55c97297006377478cddb260ec0e8c78cf41185d5c4f2620dfda30f60046e41546077d0069c61e5930e2bd7259ee5260bcd31163cf2c18ef044659ae2930bab89f9f807c6bc3d80b1865b9cb80d1c1b57e9ab157168c784d2ccb5d0e8c658e187b67c158068cb2dc15c0e8e2ba3d61307c1e631f6014fd4a60bccb11639f2c18ef024659ee2a60bcdb11e35d5930de0d8cb2dcd5c0788f23c6bbb360bc071865b96b80f15e478cf764c1782f30ca72d702635f478cf766c1d8171865b9eb80f13e478c7db360bc0f1865b966c078bf23c6fbb260bc1f1865b9e6c0f88023c6fbb3607c001865b9eb81f141478c0f64c1f82030ca723700e3438e181fcc82f1216094e56e04c6871d313e9405e3c3c028cbdd048c8f38627c380bc6478051962b06c6471d313e9205e3a3c028cbdd0c8c8f39627c340bc6c7805196bb0518fb39627c2c0bc67ec028cbdd0a8c8f3b62ec9705e3e3c0d8cfc2d8df11e3e35930f6074659ee0e601c103f63fa3b34fdb3601c003c4fc4cf93f66c40163c4fb8e5497fc3698025af27e3cf2bbd2d0606b52ffb93c033287e9ef4b678320b1e612882e5d0b3c1f133a63d1b9405e360e019123f4fdab3c159f00c01cf065b3c7b2a7ec6b46743b2607c0a7886c6cf93f6eca92c788682674f593c1b163f63dab3a159300e039ee1f1f3a43d1b9605cf70f06c98c5b3a7e3674c7b363c0bc6a7816744fc3c69cf9ece82670478f6b4c5b391f133a63d1b9105e348e0298f9f27edd9c82c78cac1b39116cf46c5cf98f6ac3c0bc651c053113f4fdab35159f0548067a32c9e8d8e9f31ed5945168ca381674cfc3c69cf4667c133063c1b6df16cac23c63159308eb5f0c4fd7dd13196bcc6392afb3341edcb2e0c45b01c328e77c4382e0bc6f1c028cb61fffa04478ce3b3609c008cb21cf6af3feb887142168ccf02a32c87fdeb131d313e9b05e3446094e5b07f7d9223c68959304e0246590efbd7273b629c9405e36460445e619ce2887172168c53804b96c3fef5a98e18a764c13815186539ec5f9fe688716a168cd3805196c3fef5e98e18a765c1381d186539ec5f9fe188717a168c33805196c3fef54a478c33b260ac0446590efbd7673a62accc82712630ca72d8bf3ecb11e3cc2c186701a32c87fdebb31d31ceca82713630ca72d8bf3ec711e3ec2c18e700a32c87fdeb731d31cec982712e30ca72d8bf3ecf11e3dc2c18e701a32c87fdebf31d31cecb82713e30ca72d8bfbec011e3fc2c181700a32c87fdeb0b1d312ec882712130ca7278cdf59c23c68559303e078c0b3de305c1883cc5f1f124b1ec98d7f304657fdec29317b8293be6b588a0ec8bf46fae313e97038c637380d1fbe8193de3578fd1efd7de472646efa3f7d1337a46cfe8192f74c65c68c33d634ed4c7545d1915cfe2f879d29e2dca8267317826cb3d91038ccfe700e3736e19537565543c4be2e7497bb6380b9e25e0992cf7845bc6545d1915cfd2f879d29e2dc982672978b6c4e29903c6545d1915cfb2f879d29e2dcd82671978b6d4e29903c6545d1915cff2f879d29e2dcb82673978b6cce29903c6545d1915cf8af879d29e2dcf82670578b6dce29903c6545d1915cfcaf879d29eadc882672578b6c2e29903c6545d1915cfaaf879d29eadcc82671578b6d2e29903c6545d1915cf0bf1f3a43d5b9505cf0be0d92a8b67ac8c637380f1b91c60cc051f3da3676462f4fbb5f79189d1fbe87df48c9ed133e62ee3f339c0e8b7b56764655c1d3f632a5bc6d58e3d7354cef4385e2fea75c5f86d8994f2ea25c3ab5586574590e645f0ef2507fee541beb26e9997fcb2656e41c0ec28ef948c2fbdcac8ef39c30f35add1bf0d411f0b9ebeace332869fa47f1e744933570f98226383caf42c94774dfce52da969df96fc90673419cf22329ea7c9789692f10c26e35941c6f33819cf43643cf792f1f426e36943c633878ca72519cf74329e14194f03329eb6643cddc8784690f18c21e31942c633918ca73f19cfc3643cadc878fa92f19491f17427e3994bc65342c633838ca70b19cf24329e67c8781693f18c24e35948c6f31419cf32329e01643c2bc9781e21e3b98f8ca70719cf3c329e24194f25194f67329e3e643ce3c8782693f19493f10c25e31948c6f328194f0b329efbc978ee22e3e949c6339f8ca70319cf4c329e4e643c53c878c693f12c21e31945c6b39c8c671819cfed643c4f92f13c46c6731b19cf03643c7793f1dc49c6b3808ca71d19cf2c329e52329ef6643c13c878a692f15490f10c27e31944c6d38f8ce741329ed5643cf790f1f422e3e94ac6339b8ca73519cf34329e8e643c79043c89e0fc775213f0ffd5a0e51bcb360e4345f3eaffafd57a3e2cb34ec71b58d6bd16b497757c9d6559f4692d94a54cc7935f6c4afb847995c1bce457081ceb48783a92f14c23e3694dc6339b8ca72b194f2f329e7bc8785693f13c48c6d38f8c671019cf70329e0a329ea9643c13c878da93f19492f1cc22e36947c6b3808ce74e329ebbc9781e20e3b98d8ce731329e27c9786e27e31946c6b39c8c671419cf12329ef1643c53c8783a91f1cc24e3e940c6339f8ca72719cf5d643cf793f1b420e379948c672019cf50329e72329ec9643ce3c878fa90f17426e3a924e34992f1cc23e3e941c6731f19cf23643c2bc9780690f12c23e3798a8c672119cf48329ec5643ccf90f14c22e3e942c633838ca7848c672e194f77329e32329ebe643cadc8781e26e3e94fc633918c670819cf18329e11643cddc878da92f13420e34991f14c27e36949c633878ca70d194f6f329e7bc9781e22e3799c8c670519cf60329ea5643c4f93f12c22e3194dc6f3ac85c7c1f88e691e793f4fd62df3ab49f276b01dd2e35abee2a84cebf5ba1aeaf50abfe45700693e699cf96d1a64ea83e8c2b546c7b16e8837ebc1a3f58eca22db23cfd83e8ef34ee17ba3013004863f8185c7c5fbb68eca794e3d8c7b7cd50d8657e6b62b8234af807f1b1cf867abdb322ff965cbdc828019eb4571106fbdd8187f99feffb8ade2eb46c35f2cd7ab3a1e63bd4caa756cd2eb6a08f9bd0af96ed1f1bc18f355ebdaacd725e3cc0ac716d025cd7f83761999d524ed2f7e636dab8e97c5c79c1e071bc7d1154f702a83f856e0d9ec80c75139d3db669b51a6d546998a20cd2628e73607e5cc837c65dd32bf0d7864c2b1875dd483da6cf3a8b18719781691f13c4dc6b3948c673019cf0a329ec7c9781e22e3b9978ca737194f1b329e39643c2dc978a693f1a4c8781a90f1b425e3e946c633828c670c19cf10329e89643cfdc9783691f13c4cc6d38a8ca72f194f19194f77329eb9643c25643c33c878ba90f14c22e379868c673119cf48329e85643c4f91f12c23e31940c6b3928ce711329efbc8787a90f1cc23e34992f15492f17426e3e943c6338e8c6732194f3919cf50329e81643c8f92f1b420e3b99f8ce72e329e9e643cf3c9783a90f1cc24e3e944c633858c673c19cf12329e51643ccbc9788691f1dc4ec6f32419cf63643c1bc8781e20e3b99b8ce74e329e05643cedc8786691f19492f1b427e39940c633958ca7828c673819cf20329e7e643c0f92f1dc43c6d38b8ca72b19cf6c329ed6643cd3c8783a92f1e411f0448d3d2cffdf009abc5387e3116fd7f1cda0e55bf29067cfb78156a0e3b20ef52ae7e4e6e7af1b7d72f51e21e65506f3921f8e3dbc9d84a72319cf34329ed6643cb3c978ba92f1f422e3b9878ce741329e7e643c83c8788693f15490f14c25e39940c6d39e8ca7948c6716194f3b329e05643c7792f1dc4dc6f30019cf06329ec7c8789e24e3b99d8c671819cf72329e51643c4bc878c693f14c21e3e944c633938ca70319cf7c329e9e643c7791f1dc4fc6d3828ce751329e81643c43c978cac9782693f18c23e3e943c6d3998ca7928c2749c6338f8ca70719cf7d643c8f90f1ac24e31940c6b38c8ce729329e85643c23c9781693f13c43c633898ca70b19cf0c329e12329eb9643cddc978cac878fa92f1b422e379988c6713194f7f329e89643c43c878c690f18c20e3e946c6d3968ca701194f8a8c673a194f4b329e39643c6dc8787a93f1dc4bc6f31019cfe3643c2bc8780693f12c25e3799a8c671119cf68329e67eb87a754bd8b87e37d0a174e6510df063c9b1cf8e3a89c497c0f33ceb13495573b0caf36185e15419aade0df0e07fee541beb26e9997fc3cb3678e62563cd2b7637b3f782109a3689b9cf29426135066996a6a1f77008f8be38723dfd3edd84ea34c0b2dbe4b1aacab3b1d94d3b6efc8fc4e4bdec541bc5eecaa8517bb2c3cbbead90bc92f5be6ad39c8ec7df63e47317b9fbdcf51ccde67ef7314b3f7d9fb1cc5ec7df63e47317b9fbdcf51ccdee7fa61f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcf7563563c8b745c5813906e1109a368dbdcf2a4df375a149c3be519f36510df053c3b1cf8e3a89ce967c8771b655a149cefbba4c1fd6bb78372daf61d99df0ddb211be69d39c8ec7dae1bb3e259ace38bf46f02d22d2661146d875b9e743bb6383877aaa91ddb0d3c2eda7947e54cb763554699165b7c9734b87f553928a76ddf91f92ad80e559ed9335b9815cf121d17d604a45b42c228da2ea73ca9f4fb8d4b8273a79adab12ae071d1ce3bf23ddd8eed31cab4c4e2bba4c1babac741396dfb8ecc4b7ed932efcc4166efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fb7ce1f8ac78e49bc0c29a80744b491845dbed94a763badf616970ee9467cc97417c0ff054c5ce93e97770e07bbadf61af51a6a516df250dee5f7b1d94d3b6efc8fc5ed80e5f75e69d39c8eceb46fd30fbbae199a3987dddf0cc51ccbe6e78e628665f373c7314b3af1b9e398ad9d70dcf1cc5eceb86678e62f675c3334731fbbae199a3987dddf0cc51ccbe6e78e628665f373c7314b3af1b9e398ad9d70dcf1cc5cc503714cf321d17d604a45b46c2285a955b9ef4770f9605e74e79c67c19c4f702cf1e07fe382a67fab99d7d469996597c9734b87fed73504edbbe23f3fb603b7866cf6c63563ccb755c5813906e3909a3687bdcf2a4dbb1e5c1b9534dedd83ee071d1ce3b2a67ba1ddb6f9469b9c57749837575bf8372daf61d99df0fdbc1337b661bb3e259a1e3c29a80742b481845dbeb9627dd8ead08ce9d6a6ac7f6038f8b76de5139d3edd801a34c2b2cbe4b1aacab071c94d3b6efc8fc01d80e9ed933db9815cf4a1d17d604a45b49c228da3eb73ce9766c6570ee54533b7600785cb4f38eca996ec70e1a655a69f15dd2605d3de8a09cb67d47e60fc276f0cc9ed9c6ac78faebb8b026205d7f1246d1f0fce750fc3ca50983474d35b563871cfbe3a89ce976ec7060f7fd10f82e69b0ae1e7650ce3cc857d62df387613b64c3bc330799bdcf7563563c03755c5813906e2009a3680781e748fc3ca50983474d35b563471cfbe3a89ce976ec6860f7fd08f82e6970ff3aeaa09c7990afac5be68fc276c88679670e327b9febc6ac7806e9b8b02620dd201246d10e03cfb1d87932df37461e35d5d48e1d73ec8f9b7266dab1e381ddf763e0bba4c1fdebb88372e641beb26e993f0edbc1337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993d3337b3e219a2e3c29a8074434818453b0a3c2762e7c9f43b208f9a6aea7738e1d81f37e5ccf43b9c0cecbe9f00df250dd6d5930eca9907f9caba65fe246c07cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6ccdccc8a67a88e0b6b02d20d256114ed38f09c8a9fa73461f0a8a9a67e87538efd7154ce74bfc3e9c0eefb29f05dd2605d3deda09c7990afac5be64fc376f0cc9ed9c6ac7886ebb8b02620dd701246d14e02cf6bb1f364fa4f91474d35b563af39f6c74d3933edd899c0eefb6be0bba4c1ba7ac64139f3205f59b7cc9f81ed900df3ce1c64f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcf178ecf8a67848e0b6b02d28d206114ed34f09c8d9da7633261f0a8a9a67e87b38efd7153ce4cbfc3eb81ddf7b3e0bba4c1fdeb7507e5cc837c65dd32ff3a6c87af3af3ce1c64f675a37e987dddf0cc51ccbe6e78e628665f373c7314b3af1b9e398ad9d70dcf1cc5eceb86678e62f675c3334731fbbae199a3987dddf0cc51ccbe6e78e628665f373c7314b3af1b9e398ad9d70dcf1cc5eceb86678e6266a81b8aa75cc7853501e9ca4918453b033c6fc4cf539a3078d454d3733b6f38f6c75139d3cfedbc19d87d7f037c9734b87fbde9a09c7990afac5be6df84ede0993db38d59f154e8b8b026205d0509a368afbbe549250c1e35d5d48ebd093c5f8b9f27ddaebe9905cfd780e7adf8794a1c9533a9d6fb75608f6bbdcaabb70dafde34bc2a8234c8f0b603fff2205f59b7cc4b7e9ed9334731635b28ac0948f70609a3686f018f8b764395bdb55e97acbf61180637adcef76cfcf996e2354b43bd5ee190fc0a20cdfdcdaad30ed36c85f07fd96eaa3c670ccdd1b76f4a6cef4bc8bce45718d4db35448dd734e8858be7fcb33deea31712ff2c88af5ee37e8e79b9f8b652b6ef5c9cb6f0c458f692a8f74d5c7cc74e95bd8d5e97ac5feda3539b3af5bc14f73d693fda18652e80342da0fda884f6c3d656b8de37f302fbbe991f54b767c255ac75f3dec4675a977467213db639fdf52fee9ffda1acaedac5a86b126c17cdb6dba5f7e6fd3133ef22f0e52ca967b6eb38f471a0857b200137d6c7fadccf64ddb67b08030d1fd93cc36d7dd6e2e3200bf720026ec6fd7a90e1239b679fb75f0fb1700f21e066dcaf87183eb279f679fbf5500bf750026ec6fd7aa8e1239b679fb75f0fb7700f27e066dcaf871b3eb279f679fbf5080bf708026ec6fd7a84e1239b679fb75f975bb8cb09b819f7eb72c34736cf3e6fbfaeb07057107033eed7b5ed2765daaf1dddff4ef793a2676aaae99ee159a7fe64be579fcdf327af018f8b3ae5a81e241ddd734df7939ae30f9c35bcc2f1071cdf97adf1bb44929f67f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b667e667cbf17fb5724ddeb248ca2397e363fdd9fd156af4bd6af9efd9d7d99cb7c5349ecf794e7924d8e0248f3efd756a79dafd9b07f11fb454f1b9acb6d698e3f2df3925f219407ebbfab77b5a3ead6eb96bc8b63cb3b55e1c6e354f2d2701d1705d5dbf994511ef4f464ecf99feb699ee1e949c77967fbbe04f238e88b2d7154ce745b70c22893e97111a46901e53ce1a09c7990afac5be64f008f4cf9c0e3aa0e06064f60f147a6d1643c83c9781e22e3b9998ca73719cf55643c73c87852643c6dc8782e22e39948c633828ca73f194f5f329eebc978ba93f13425e39941c6d3858ca7808ce719329ea7c8781e21e3b9958ca70f19cf1d643cd790f1cc23e34992f124c8782693f19493f10c24e3b99f8ce746329e9e643c9793f1cc24e3e944c6d3888c673c19cf30329edbc9781e23e3b98d8ce76e329eebc8781690f1b427e32924e3994ac65341c633888ce741329e62329e5e643c5792f1cc26e3e948c6d3848ce759329ea7c9781e27e3b9978ca739194f37329e96643c9792f14c27e36940c633868ca72d19cf10329e87c9785a91f1dc42c65346c6733519cf5c329e12329efa7827201b9e8bc9782691f18c24e31940c6731f19cf0d643c3dc8782e23e3a924e3e94cc6d3908c671c19cf50329e47c9785a90f1dc45c6732d19cf7c329e0e643c9790f14c21e31945c673868ce749329e07c8786e22e3b9938ce70a329e59643ca5643c8dc9782690f1b423e3194ec6d38f8ce71e329e66643c5dc9788ac878a691f1b426e3c923e04904e77f4b2701ff7f0d34f9e60b7e2f2cdfb23ee9a796f4eab838a7f9f9ebceb7acfba485017d3a0e6529d3f1e4179bcef9ce4d9e5eafcc4b7e85c0719284a73519cf34329e22329eae643ccdc878ee21e3e947c6339c8ca71d19cf04329ec6643ca5643cb3c878ae20e3b9938ce726329e07c8789e24e33943c6338a8c670a19cf25643c1dc878e693f15c4bc67317194f0b329e47c9788692f18c23e36948c6d3998ca7928ce732329e1e643c3790f1dc47c633808c672419cf24329e8bc9784e93f19490f1cc25e3b99a8ca78c8ce716329e56643c0f93f10c21e3694bc633868ca70119cf74329e4bc9785a92f17423e3694ec6732f19cfe3643c4f93f13c4bc6d3848ca72319cf6c329e2bc9787a91f11493f13c48c633888ca7828c672a194f21194f7b329e05643cd791f1dc4dc6731b19cf63643cb793f10c23e3194fc6d3888ca71319cf4c329ecbc9787a92f1dc48c6733f19cf40329e72329ec9643c09329e2419cf3c329e6bc878ee20e3e943c6732b19cf23643c4f91f13c43c65340c6d3858c6706194f53329eee643cd793f1f425e3e94fc633828c672219cf45643c6dc87852643c73c878ae22e3e94dc6733319cf43643c83c9784693f1e41b3cf87ff56e8f3caf7d42ff16c0ff97e987099aea75491a3946ab7b27c70c4d95f7a8a3f21e0baaa732983f0ae515f663c073cc11cf7183c7ccbb10e203c1b32386a6180f3b623c6230cafc616014ff8e00cf11473c470d1e33ef42880f02cf0e199a623ce888f190c128f3078151fc3b043c871cf11c3678ccbc0b213e043c3b60688a71bf23c60306a3ccef0746f1ef00f01c70c473d0e031f32e84f850f06c9fa129c6bd8e18f7198c32bf1718c5bf7dc0b3cf11cf7e83c7ccbb10e2c3c1b33d86a618ab1c31ee311865be0a18c5bf3dc0b3c711cf5e83c7ccbb10e223c0b3dd86a618773962dc6d30cafc2e60acd2f1ddc0b3db114f95c163e65d08f172f06ca7a129c61d8e18771a8c32bf0318c5bf9dc0b3d311cf2e83c7ccbb10e215e0d97643538cdb1c316e3718657e1b308a7fdb8167bb239e1d068f997721c4fb8126bc5d40dbaae39d41dba2e39d40dbace3a5a0bdaae31d41dba4e329d036ea7809681bc40fd0d6eb7807d05ed1f1f6a0add3f1aea0add5f16ea0bdace3dd415ba3e33d407b49c77b82f6a28edf09da6a1def05da0b3ade1bb4553a5e06da4a1def03da0a1dbf0bb4e53a7e3768cb74fc1ed096eaf8bda02dd1f1bea02dd6f1fb405ba4e3f783f6bc8e3f00da733afe20680b75fc21d0c6eaf8c3a03da1e38f80f6868e3f0ada9b3afe18686fe9f8e3a07d4dc70780f6751d7f12b4b7757c3068dfd0f1a740fba68e0f03ed5b3afe3468dfd6f191a07d47c74781f65d1d1f0ddaf7747c0c68efe8f833a07d5fc7c781f6031d1f0fda0f757c0268efeaf8b3a0fd48c72782f6631d9f04da4f747c32683fd5f129a0fd4cc7a782f69e8e4f03ed7d1d9f0eda073a3e03b49feb7825681feaf84cd07ea1e3b340fba58ecf06ed573a3e07b45febf85cd03ed2f179a07dace3f341fb44c71780f61b1dc7ef68ff56c78b8378dbf74f83eaa918f296fc549adfe97823238d2c5b00690af5cd4875bfa328a86effe578a03469ffb78226edff16d0a4fddf0c9ab4ffaf8226edff26d0a4fddf089ab4ff1b4093f67f3d68d2febf029ab4ffeb4093f67f2d68d2febf0c9ab4ff6b4093f6ff25d0a4fd7f113469ff578326edff0ba095e9f82ad0a4fd5f099ab4ff2b4093f67f3968d2fe2f034ddaffa5a049fbbf043469ff178326edff22d0a4fd7f1e3469ff9f034ddaff85a049fb3f163469ff9f004ddaff374093f6ff4dd0645ffb14343926bc059a1c13be069a1c13be0e9a1c13de064d8e09df004d8e09df044d8e09df026da48e7f1b3439267c073439267c173439267c0f343926bc039a1c13be0f9a1c137e009a1c137e089a1c13de054d8e093f024d8e093f066db28eff04343926fc14343926fc0c343926bc079a1c13de074d8e091f8026c7849f8326c7840f419363c22f409363c22f419363c2af409363c2af419363c247a0c931e163d0e49820c78826a0c9fd6a3525bfe05414544ff99097309505f11e73702a83f87c28bb4c8bc978d690f16c25e3b9998ce72a329e36643c1791f18c20e35941c6d39f8c673d19cf6e329e5d643cd793f11c23e3394ac6d3948ca7808ce745329ecd643cb792f1dc41c6730d194f828ca79c8c671919cf40329e75643c3bc9787690f1dc48c673848ce73019cfe5643c8dc8785e20e3b99d8c671319cf6d643cd791f11492f15490f12c21e31944c6f33219cf27643cdbc8788ac9780e91f11c24e3b9928ca70919cf4a329e0d643ccdc9785a92f15c4ac6d3808ca72d19cf22329e21643c2f91f17c4ac6d38a8ce716329e2d643c07c878f693f15c4dc6739a8ce762329eed643c6f92f12c27e379858ce706329ecbc8781a92f10c25e3594dc6f32a19cf3e329ebd643cd792f15c42c6f31619cf52329e33643c6bc9786e22e3b9828ca731194f3b329ee1643cabc8783692f1ec21e3a922e36946c673828ce738194f11194f6b329e3c029e047004a0c9ff1b8026efeb9e014ddeef3d0d9abcebbb1d347937780168bfb668f9163e61f818347937631e6872bdff1168f2cce05cd0e4bc41f257f38b9a9fcf9f6f2967030bff479672ceb32c8bdb5b96290be2ddde98575970fefbd785c0318f84a735194f1119cf71329e13643ccdc878aac878f690f16c24e35945c6339c8ca71d194f63329e2bc8786e22e3594bc673868c672919cf5b643c9790f15c4bc6b3978c671f19cfab643cabc9788692f13424e3b98c8ce706329e57c8789693f1bc49c6b39d8ce762329ed3643c5793f1ec27e33940c6b3858ce716329e56643c9f92f1bc44c633848c6711194f5b329e06643c9792f1b424e3694ec6b3818c6725194f13329e2bc9780e92f11c22e32926e3d946c6f30919cfcb643c83c8789690f15490f11492f15c47c6731b19cf26329edbc9785e20e36944c6733919cf61329e23643c3792f1ec20e3d949c6b38e8c672019cf32329e72329e0419cf35643c7790f1dc4ac6b3998ce745329e02329ea6643c47c9788e91f15c4fc6b38b8c673719cf7a329efe643c2bc8784690f15c44c6d3868ce72a329e9bc978b692f1ac21e3594cc6936fe139ed8847de9d9475cbfce9af78de478dbc8f5e20791f36f23e7c81e47dd0c8fbe00592f77e23effd1748de7b8dbcf75e2079571979575d2079ef32f2de7581e4bdc3c87bc7059237f3798bfa3e865c639fd0bf09f83f8edbbddd11e3698351e6b703a368f85da4fe8e78a2ce79fa13e4adbc18a8e372af2f01ff1f088caeea547f8351e66d750ac7a51ee88827ea5c6d2041deca0b79c64bfaca13f07f1cf7cb559d1a6830cabcad4e1d069e418e78a2ce310711e4adbc907724e459d304fc1fc7057155a706198c326fab5338aed510473c51e7c64308f2565ec83bcff2ee5802fe8fdfe97655a786188c326fab5338cec450473c51e7f44309f2565ec83771e4db0209f83f7ee7d4559d1a6a30cabcad4eed059ee18e78aa0c9e2a8b175f56deca0be963d8a37f13f0ff11c0e8aa4e0d371865de56a7aa806784239ea86ba81104792b2fca755cfa1613f0ff72607455a746188c326fab53bb80a7dc114fd4b55f3941deca0b79c65d9ecd4bc0ff71dc5c5775aadc6094795b9dc271e82b1cf1445db356d443de51d75ff59177d4b5447de41d755e5c1f79479de3d547de51e72bf59177959177553de61d751ca98fbca3da44bf7ffbfd3beebcbfcc6349959177553de6fd65eedf5f669bfa65b62dfebcc5b76bf595b73f6fe13e6ff92cbebc93787dda24c6f5e27d816dfa17efed6c054deeb56c014dee976d064dee79be0a9adcb7de049af43d6c044dfa8fde024dfa00b16f52bef97b1c3479260dfbc4648c89a3a0c95814d817f32b1d3f0cda1c1dc73e80d93a7e10b45fea38de7b9ea5e3fb41fb858eef036da68eef05ed431ddf03dacf75bc0ab44a1dc77b3c33747c17681fe838de5b98aee33b407b5fc7df046d9a8e7f0ada7b3a5e01dacf2cda541ddf00da141d5f0fda4f75fc15d026ebf83ad07ea2e36b419ba4e32f83f6631d5f03da8f74fc25d026eaf88ba03dabe3ab417b57c75f006d828eaf02ed873abe12b4f13abe02b41fe8f872d0beafe3cb401ba7e34b417b46c79780f68e8e2f066d8c8e2f02ed7b3afe0968a375bc1cb47c1d1f015a031d1f0e9abc17341434f9d6eb10d0e47dee41a0c937df0782d644c7fb8326cf92e37834f27d491c8f46de01c4b16ce43bd31f8126df6ec0b167647c101ca346be71f52bd09aeaf81cd0e45bb2b34193f7c57f099a7c537e1668f29d9f5f8026cf86cf044dbe17f92168f28ee1cf4193ef58578226df8698015a331dff0034f966d574d0e4bd9df741936fd54e034ddeb77e0f34f966fdcf402bd6f1a9a0ddace35340bb45c77f0adaad3a3e19b4163afe13d0e4db13934093ef2dfc1834f966d88f406ba5e313419377279f054dc6b77917b4363a3e0134f98edc0f416ba7e3e3416bafe33f00ad838e7f1fb4a48e8f03ad44c79f012da5e3ef80d651c7c78056aae3df03ad938e4bbba0f63fb55fbea6e7cb82f8cea7547e678373a73c63be0ce2c2803c719e6315010fe6752af6b2a792f8cc53be5eafd4975390f789d8f34ea6f33ea9d755a0d77bc2c8bb00d2fceddaea6d734c6b0df472a78de5f01a58d62dcbb405fdb8b1eea6babc271d95f784c124dce883a469a01b4d752c3ba8e38e9ea54aa97d40ea5a001ee25406717c5e307eaf52493c07af0dcf49e0897f3f4996b8aa13b86fc5d986d8ee019975ad08d2e09893c71df887fbbaac5be6253fcfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6ccfccc8a47fa13b0cf5cd2bd46c2281af6759d8c9f27897daeb27ed5afb311fa755cf7ef35d4eb6d6794b900d27c047d4e5b74bc10fe2fdb2d6a5b3ae827ac715b4a7e85509e93c0e3a2bf380ff292759fb27821f1e2d8f24e55b8f1389554cf99a83ef6d386afaf593c75b5bf62df2d7a8afbeb598307fb460b81f775fd9b80f5bc0e6570b08fd7582f243fdc974e8126f1b3c0e8623be3b144da83b67a1efba625cd69edadf48bc7bfed534997edc631285359707efd2e80346f40dbf7968ee3b321af816fef5afe2f534dfdd4e29f2af391f8cb9cc467e065fb1eb1e47d085863cafb9ce7eff374907c442f80f80fafab4e2be9c40ff15ad8f1fbd6c86e2e77d658ae08d21cb594bf2c88b7fc470c9e2306b3da266f433d7b178effaedaa4a3111eb5058f240d9e07b9fa668fd9460a87e4a7d2c8f66f64a4c1731649f301b451aa2cd2ce4b39f1d9163c06b83a8ebd66944fe66de7c62781d12ca3aa1fb32fabe6bdd09ffffa03b417713ffff507a843f89c5660acbf1dac5fb81a05d1c71649f367e338eae21a03b7a5e927fa2c69fe02edd00dfa61dc6ccef5bfacebb6a873fda30e7812c1b9d7de6aaae9f88ec798630e781c9533693b761d37ca5404695a40391d9cc7d4f81ee111c8dbc536472fe41cea84e14501a4f99f46db11e5235eab9eac97b2a4ace783ed2c6591349f19edd461074c2eb7db2128935aef294b59254d8366d5be34d4f1046c27bc6f72a5e5ff32d5d41e887faacc07e22f7312dffd94ed7bc092f73e608d29ef73de3b95f37dc947f402885fd1ac3aada4133fc46b6157fb88bcdb86ece672278ce58a20cd414bf9cb8278cb7fc0e0396030ab6dd204ea99d42397ede6c1088fda81479206ef1fcbb11ddf6fb41df75d3c5f5dd371ff14309aed269ebbb86433bf0b6bde43b59d0f4a1a3c279334b768e6a6c1b9efe3609bb3d1ed33ff25f8fe6a00e5088cb206069b239f4bd167f31e601bd04d9fe57fb66b86b6e0737ddc9f166ef35a00af4784db3ccee0f5480970ab7a2fdb09ef238af686fe75744e5e8ad75b728c31cfc9f13e5a1760c76585eb754b59c403499b0fff97ff7d16d8af4f3e6f39337ec65846f9fb8691ce964f135836b677354a924993ffb4c1896db3a4b3f92afb01d6395997b9afe07d5149d3d76893ccb46adb4f6d5aed8f6c47f10edb13ac93aefa60ce18e59779c94f31ca3da033c0e3e23e4ab6ef4fb9becfe6a88d4ee2bd8bf8beffd06d94ed9ecb6b865745c1f9f7335cb6e9517d39272c79c7e7456ab4adbfdfe6457df6f7477971dc92777c5e74aeb0b573362f8e59785cdc7fa9c98b6396bc63f462aced9e88cd8ba3161e57d7db515e1cb5e41d9f175dacf7ab6c5e1cb1f0b8baee8af242f2cb96f918017313231e4fdea5a3f0be544d5e1cb6f0c47f4faa662f0e5bf28ecf8b92ce35f57fa217872c3caefa31a3bc3864c93b3e2fba76b3ddbfb07971d0c273b09ebd3868c93bc67a31c6767fc9e6c5010b8f837b8d357a71c092778ce7879df15e634d5eecb7f0ecaf672ff65bf28ed18b72db7d509b17fb2c3caeee834679b1cf92777c5e8ceaa4f2de5b0b2ff65a78f6d6b3177b2d79c7e745795795f79e5a78b1c7c2b3a79ebdd863c93bc66ba874bda8aa851755169eaa7af6a2ca92777c5e8c4e9f6bedae8517bb2d3cbbebd98bdd96bce3f322993ea6eeaa8517bb2c3cbbead98b5d96bc63ac17e9ebc99db5f062a78567673d7bb1d392778cc79174bdd8510b2f76587876d4b3173b2c79c7e7c5d8f4fda7edb5f062bb85c7d5b88c515e6cb7e41de33d9774bdd8560b2fb65978b6d5b317db2c79c7e745c7f431756b2dbcd86ae1d95acf5e6cb5e41d9f1763d27d625b6ae1c5160bcf967af6628b25ef18cf3bd3edc5e65a78b1d9c2b3b99ebdd86cc93bc6f3cef4fd8b576be1c5ab169e57ebd98b572d79c7d876a6cf3b37d5c28b4d169e4df5ecc5264bde319e77a6bdd8580b2f365a7836d6b3171b2d79c778de993e8e6ca885171b2c3c1bead98b0d96bc63ac17e9b6737d2dbc586fe1595fcf5eacb7e41de37dad74dbf94a2dbc78c5c2f34a3d7bf18a25ef18af47d2f7f8d6d5c28b75169e75f5ecc53a4bde31f615a5cfc1d7d6c28bb5169eb5f5ecc55ac8dbd5fb3a92873c8bd5daf0a200d2fc87f12c56948fb20e7cae0ccbf272ec65c93c57b626a22c2f435924cdff369ee55be380c95159d375e62528935aef594b59258d7c085ff992afe309d826af836f9759fe2f534dcf20897faaccabe32f73baaebe009c65900fe6bd0a5863cabb04f39667d3251fd10b20deb479755a49277e88d7c2aef69117751cd9cde5d61acb15419a172de52f0be22dff6a8367b5c19c7eef01ea99d423376d5786e9c5088f5a834792069fd93beb88c77c865038243f9546b67f23238d2c5b0069aed31eca73bbf21ca49433119cffdca4a3b6ac04d965dd322ff9e1fbb2a780d12ca3aa1f83e1d9cf0e5a6f1f546b25c20deb29353455d64e8eca2a79c9ba65be1330ca9814a5f5cf98aa2d63478351f17471e0198eb321534dc78b2ec0d3d9018fa372a68f435d8d327532ca540469f0ddc6ae0eca9907f9caba65be2be4ed629ba317724cbeddf0a200d27481f6ac261f651daafe965acad2c3715964ddd22ef5a887bcbb1979a78cbc13c1b9db39086adebfba01737707cc6abd3de35f6f7affba53af4bea94e4938232f5020fe22a13e62de779928fe805107f18cef3249df821c72f61577559b625b29bcb7531962b82343d2ce52f0be22d7f4f83a7a7c1acb6491f38b773b03fa4eb400f8343e653e05dcf08ef7a807792068f7f1d1c79d7dde091f90ec023e7385d41937305e14fc0ff93f5c06db67b5d2ddca27503c60e16c6d2f819d3e73a1d0c46992f0546d1ba034f37479e99dbfa76c31f3c2e3732d2c8b20590a61c8e8d094b5ab5dffd2b944bc62e8c719c9f749bdec8815f38ae6200fe04868701f825e56ce880e7e2a07a6cc5199553a6973f33a6ff984cd7a3a0151898f89b6729463e68186f60d182e0dc21240b409321241b82966fd88243574a7a19d2ce855de887acbbc0e06c022c71e68dc36fca5453d5690c3c2eaab2aa3a97e875e9aa3368faf8ca31583f1a1a9c75a93bea7f0d6a4817b52e57dbc1dc27ca60deac83058ef26f00e52d8379c94f6d9b421d9f5a5ef16c9fe9cfcc9c346672e50c34cadcb1319e179cbb01ccdf28c35ded745801b0c0d8383434ca850d86fc4f36ccc5f17396e218b7a63701e427d3c5e0db450e7c53eb97b16a2bca274eec3773d4c4f1157d674eaea81c3f65326ecd268673515b5afedf08345b138f69d584cd162edbd8a2d9261c15b8096872e4ba0834e1b918b4061097f4e69671525d5bc0fa659752ff53e634d4056f1c545701391cab7655edbfea54eed220732a745990d99c6aa86135b4b01a4a580d1dac860a56a36caaaf5aa89e1c75f6a486f65543f9aaa17bd550bdc5416628de5b82eaa176d5f42e70de16644eb5d4d0b9ad82ccd0b8ea76a57a5d5d7dda4d7d4a439db2ab534b759aab2eebd42588baada64e3dd52d0b75fb4a9dc6a9536475faa74ed9d42588baf450978aead2a9b7f6ba4f18ee0ac3dd61b8270cf786a16f18ee0bc3fd6178200c0f86e1a1303c1c8647c2f068181e0b43bf303c1e6486921e10862782cc50d34f069961a807079921aa9f0a32c3570f0b32435b3f1d6486bd1e196486c41e156486341f1d6486ce1d1b6486da1d176486f09d106486069e1864861c9e1c6486329e1a64864856c329aba197d590cc6a486735fcb31a2a5a0d213d370cf382ccb0d30bc2b0300ccf85e1f920336cf7e22033ccb71afe7b5990192e5c0d23ae8617575d09aa0b45752da8dbf6aafb4add5a565d5aea96b7ea8a555dd3aaab5e3dbaa01ee5508fb6a8477dd4a34fea5130f5689c7a54503d3aa91e25558fd6aa478dd5a3d75541e6d1fcbd41e6d50df52a8b7ab547bdeaa45efd3a1c645e0d54af77aad725d5ebbfea75e89341e676b67ab55e7571aadbd8ea96bebad5ad6ebbaba1e5df0ac3d7c2f0f530bc1d866f84e19b61f85618be1d86ef84e1bb416658e27782cc10c83f0832c328abfaa8866cfe719019f6f9a7416688e9f782cc30d51f049921b13f0c32c36aab21b8d510debf0e32c380ab61bfd5f0e7bf093243daff360cbf0bc3df85e1f761f84318fe3e0c7f0cc39fc2f0e730fc4318fe310cff1486bf84e19fc3f02f61f86b503df4353616bfd73337ebf9f2caca3193a65616574e299e347362e5f8a913e716cf1e5f39ae78caac31d3c74e9c321b175ea39b2719b7bbcff4e9e5738bc74f1e3d664ef1949995c553c6168f9a3273f2e8730ed4c7f442d79f9f63f9e8d1d1997d23ff0b907ea78e99bea7979311d11fa8b96c3faf8b211fd565a1ff55c702cdd1472fb97d322073ae5b3c63e294cae264f1e4f06f78709d327bcce8f6c5f8bf19a1c9332a8b6754964faf2c1e3b7dcaa4e292f6b8de8f1bd7a110ffded80dcc2557d5cd9ccff4f8ce75aa62a7aeab8303ef5f5737d2df5ff70548ff54c74cff475d4af87febb2d0cdcdea46d8a659a42d33668eaa9c5e5e5119bd70f28b2cdcb9591d8a796f1d8bf9dfeb92d97fd665a16b9bd78db073f33a6436328bcc82ff07d6af973f08fc0400", "isInternal": false }, { "selector": { "value": 2603445359 }, - "bytecode": "0x1f8b08000000000000ffed9d77701cc795c6679118565810cc99902c538ce06291c104e64c9992ac1c98409116495024942ccb922cc939e76c39cb39675bce392739e7ecf2d5dd3f57bebaab525df76c3fe34373668d5dcd03df60df543d6ccfdbde79bffee64dcf6cf7ec20131497bf1acbb872adb1c705672ff47ebf7bcd3fb6a52dc16de539393329e1ac4909676d4a38eb52c2599f12ce8694704e4809e7c494704e4a9033037c5cbc9353c69b4d19ef79413af2b631259cb9947036a584734a4a389b53c23935259cd352c2393d259c3352c23933259cb352c2393b259c7352c23937259cf352c2393f259c0b52c2b930259c8b52c2d99212cef353c27941829ccb8193c6c82f74af8f77af8bddeb45ee75897b5dea5e97b936d6b975bbcd15c6561a6bf5de5b65998dd9c1f282f75ebbb10e639dc6badc7b2deebd6e633dc67a8df5195b6d6c8db1b5c6d6195bef34d9606ca3b14dc6361bdb626cabb16dc6b61bdb616ca7b15dc6761bdb636cafb18b8d3dc1d83e639718bbd4d865c69e68ec728fe50a63571abbcad8d5c6ae3176adb1eb8c5d6fec0663fb8d1d3076d0d82163878d0d183b62ec4663478d1d33f6246337193b6eec84b193c6068d9d3276b3b1d3c6ce181b32768bb15b3dcd6e3376bbb13b8c3dd9e3bcd3d8538cdd65eca9c6ee36768fb17b8d3dcdd87dc6ee37f680b1a71b7b86b1671a7b96b1671b7b8eb1e71a7b9eb1e71b7b81b1171a7b91b1171b7b89b1971a7b99b1971b7b85b1571a7b95b1573b163a105e63ecb5c65e67ecf5c6de60ec41636f34f626636f36f616636f35f636636f37f690b177187ba7b177197bb7b1f7187bafb1f7197bbfb10f18fba0b10f19fbb0b18f18fba8b18f19fbb8b14f18fba4b14f19fbb4b1cf187bd8d8678d7dced8e78d7dc1d8178d7dc9d8978d7dc5d8578d7dcdd8d78d7dc3d3fc9bc6be65ecdbc6bee37cdf75afdf7375e998ffbe7bfd817bfda17bfd917bfdb157ff116ffd27defa4fddebcfdcebcfddeb2fdceb2fddebafdcebafddeb6fdceb6fddebefdcebefddeb1fdceb1fddeb9fdceb9fddeb5fdcab9d576b9f512c4f0c8697fe20a13ea963a0d78eab93d8febc9d9d13aa75efd16b8bf3d7b9757acd387fbd5baff7fc0d6ebdc1dbce44b73ed1f3e7dc7acef34f71eb533cff54b73ed5f34f77ebd33dfff96efd7cf067dd7bc586155facafd6b932e0a3fcac015fbdf3d582af813607be09ce570f3edabf0de09be47c13c037d9f926822feb7c93484b63e7395f7f9054aee40fd8ed3626bd5d3717914b9ef7a0dd6e1313ef94e4790fd9ed3633f0dafc98eab6d5087933cdf972e09bee7c4de0735dd0bf8e39eb9be97ccde09be57c53c137dbf9a6816f8ef34d07df5ce79b01be79ce37137cf39d6f16f81638df6cf02d74be39e05be47c73c1d7e27cf3c077bef3cd07df05ceb7007cd45f2e04df85ceb7087c746dd7023ebace3b1f7c74cd7781f3d97e626206e2393ff551613cea9fc1f778ea9bc1b798fa65f05d447d32f896406cf22d857e857ccb9c8ffa28fb5e9f2bf707491d1385f0185e9df476cd96ed76d726bfdd700e6c5d30ac753fc4590d5aad77e504ef0f69c3d8741d4371c85f07e51d5097ea911e749e2176dbefaf71e5f5253ed7e77d2e0775d644b4bf3f48b6fd6b3d9eb51e733db49f29678f68ce8e7a293b67af80ba7eeed135cf78ccd9ddc0917cceb6b76bce8e7a293b6707a0ae9f7b74dd3b1e73f66ae060c8d96e9e9c2de435678be36041109d7bf4dd673ce6ec51e0483e673b356747bf949db3f7425d3ff7e8fbef78ccd95b8123f99cedeed66b83512f65e7ec0ba0ae9f7b3416331e73f67ee060c8d983dacf8e7a293b675f0775fddca371c1f198b32f068ee473b6972967db356783e29c661044e71e8d518fc79c7d103892cfd9433a3e3bfaa5ec9cfd04d4f5738fe64bc663cebed795ed3cc377dd3cc37cf07dcff916006ff2b97db88d29b7db34b78bf77a0441748ed2dcdd78cced875dd9e6f10fe0de03f2fd90ee4b00df8f9cef02f0fdd8bb6783e918e8d26360f46d2af718f805d4f57399e691c7e331f03de060c8d96ecdd9d1b7a9dc9cfd1bd4f5738fee69188f39fb6be060c8d91ecdd9d1b7a9dc9cfd27d4f5736f892b8fc79cfd872bdbeb859fb9eb8565e0fbb9f32d07df2f9c6f05f87ee97c2bc1f72be76b05dfaf9d6f15f87ee37c79f0fdd6f9dac0f73be72b80eff7ced70ebe3f385f07f8fee87c9de0fb93f37581efcfced70dbebf385f0ff8feea7cbdce67efc9a37bafbee27c76df9246fd41b2fb96eeb1a46dd3fa8a3188dde4c56e1ac3d8cd5eece688d82b19626721062d196fbd1fca2b7979f239e0c158ab928fd56edbde1a8cbeedab8027cfd0f62cc4180d4f1e78da92e709cf9f85e4b71beee3564fd32cc46a8576b533b42b03b168dbb44ef172e0c3febb3d82b12379c6420662d1b669bd0318c987e7133aafd3f163cf878b33c3bc0cc752784d44f1e8f94fc4b10afc54e791e9c36ccb1c5b23bc8fe7d636cfc79497615e502cda36ad53bc46684fdbd8331646cb98f718b9fa880cc4a26dfbb1f1785f39f69a8d6abfe6c0770efaa442a57d5223b08dc5754adcbe96129be37c958118d4b791e605f0539d99ee0709b66fdb06fd2ec3f15728f7fa0dfb83e4f3b890c7e37a343cedc0c371ec331daf793cef3f1a249b6b9d9e566d9e5639a8d301fa7532e857ea3a84e229b3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb37c66bcff02e737a9de0a218ce42b000fc7387ff80c28b72ddabe9dd7f929cceb243f6f51c8e39c25dd63b8d46b731dd4f97b66b8ee2f613edd9f1bc439cd15bcda8dea3e8bc6e0ecb958ce39c4b879e0a8f9cb96c462170e71cdb7d97bd4ec73c85a3d5d574468ca709fca084d339ea6789fe2728fc7e6e99cda61368eb9bf72e722512b2a2739b787f718f0ee9762ff41b950138cec3ff03cd39578ec917398345fdee5c5ae833aff9319de37742f2afdaf3bff9e275ba7dbdb367d6629f8bbbd6d4f719f258e7a6ffbadf059aaf37fd0a7bebe26f897661cf77f60bf1c405b71e98732ce9b277f1e2ecee3b797c1d3093c1cfd0cd3f5461e8f81a4e7f1bb3dada2ae63a84e17e8d7cda05fd4b528ad533c6556666556666556666556666556666556666556666556666556666556666596cf8cbf1525d62cd42b08611ca37b1fc2f90c7afe0b6ddfceeb3cb766382ef73c1ccd392df3da5c0775be5d335cf785aedc189c7dbf43dcbe6498cf2bb92f295e23b407e782b87ecfdde1f174446841e596c46217e7f193d778781ebfddd3b510a129d7f18a73aca8291eaf6d1e0fce8d360667df5b9285ed8cc5bd43717941f1f058ea001f95f1f7d11cfb19cf25fe7d3d140fe7af1f72da4e09b8f67d21cfd96ff4409bfa83b3f3bb0eeabc07fabef7b932dec381f78e3c1cf13e2da5e6a9493f9667d9e58bf3beab81b31fe260ec35c09a50ec368c9d714671c85f07e5cfd40cd7a57aa407694decf618a1678221bbffb936ef7339a8d31bd1fefe20d9f6f7793c7d1eb3dd271f843c7b18ceff5c7d526f8c464b4123aa83d7415ce74efffe4affbe42ec471bbc3af4d93aa8f315e8a3e2ee1f8d3a07709fc768db51e731ffbc309afb3cabfd3ead47a0bf48fa3ead472087f03eadc0dbfe32d83e713504f1e716aaf3736ffbfe35397d06ef03a33abf82fea2c9ddb3d8189c7dfd8df74c8dc5f7abb8fba4291e5ed7d07ba369bb65ee81fafd0932634e200b5e27509dbf78fbac3b867b55c467ff1ef359d28a9e4385df5f7cfdac0ebdf099fe4474281ef37d5e5b28af7ba12d54e7bfbc6bc0e4af5b8ad780c9b775e43509f5031d116da53aff0dc7da3fe11a8ff6137eef68a83dfb7d5a4a5d03927eb6cd63fd7c608c2de1f9c0f5b5c375fde7fc92d6e53e1fb8cbfb9cc4e703ff2fe45903dc87ced557af89d16819684475f0b741741ec167f9468d759cabefd3782d85fd731368caf5bb03fffce78fab455d232cf334c66b84e98e798ad3d9afeb8f17d2f932c9fb86f1b712ad10177f2bd1caa4671e74eb8775bc2e3897b1f34cb1e39e399d1f83d871cf9c1e8bd8cd5eece6318cad9aabe6923467782672f8fb337c66a95d4a5d9712430e3e579302c6da1430d6a580b13e058c0d29609c9002c68929609c9402c6c92960cc02e3b93cb733e853a8541faefd55ea5a036333fcef9242b9ffbf83f97fa994bcf6c1d80cdfe9422d5606a3d702bfe7713cfba1dcfff5420cf8bf0ba6a680715a0a18a7a78071460a1867a68071560a1867a780714e0a18e7a680715e0a18e7a78071410a1817a68071510a185b52c0787e0a182f4801e3e352c078610a181f9f02c6c5ca9808e3725ec642a58c9687e37ffe3d96ff39c6c0938fbae794e9fed9b2ffdf1af3f349db2a7d6e1cde5bc2fb3fe11edbb3ed38ee1d29f7d976a5fedf2a1363a15246ae7b81f0bea3d1f044dd1fd4c6cb58a89491ebf72ff81bbdd1f07481669d119a3130162a65e4ba57aedc7b39f19efeae08cd18180b9532e27dd509f2849a7597c1d3039a754768c6c058a89491ebbee42cc4180d4f2f68d613a1190363a15246a6dfb6859af596c183bf01eb8dd08c81b15029a3e559cda4595f193cab41b3be08cd2431224fd2cfc9ee8b88c5f19bc172db4e0cc83829058c9353c088f74970f45fa5ee93e8e3d5a750a93e5cfbabd47d12189be1f731a116f87b887fa7c55a5e9e92f74960ec754c5ae0ef55fe9d16eb8087e3f7335988311a1e62c8c1e7a6a680715a0a18a7a78071460a1867a68071560a1867a780714e0a18e7a680715e0a18e7a78071410a1817a68071510a18f1bb2ac3b562c9ef2febc679ecb8ef2ae33d76dcf792f11e5bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae795e0db135cf35cfab21b6e6b9e67935c4d63cd73c97143b0d63fcca38fe1891a725399e3cb61d63f50b687b7f044f86a9ed186b8380b61343da18d7a78071750a1855c7e23d8895305a9e8d4c3c1bcae0d9083c9b98783696c1b309783627cf13e6d4a632788821079f5b9d02c6f52960541d5547498caa63f5e8a88ccaa88cca782e18d3d0872b632af2b15029a3e5d9923c4fa8d9e63278b68066f4b9b61430f6a780713d2f63a15246cbb335799e50b32d65f06c05cde8736dbc8c854a192dcfb6e47942cdb696c1b30d34db1aa1190363a15246cbb33d799e50b36d65f06c07cdb64568c6c058a894d1f2ec489e27d46c7b193c3b40b3ed119a3130162a65b43c3b93e70935db5106cf4ed06c4784660c8c854a192dcfaee47942cd7696c1b30b34db19a1190363a15246cbb33b799e50b35d65f0ec06cd764568c6c058a894d1f2ec499e27d46c77193c7b40b3dd119a49655c9d02c6f5296064d6b15029a3e5d9cbc4b3a70c9ebdc0733113cfde32782e069e2724cf13e6d4c565f010430e3eb73a058ceb53c0a83aaa8e921855c7ead15119955119cb63ec4f01a3ee6b6594cac8f0fdaae46f912e1ee7b19bbcd84d55123beeb748e33db6e6b9e67935c4d63cd73caf86d89ae79ae7d5105bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae795e0db135cf35cfab21b6e6b9e67935c4d63cd73caf86d89ae79ae7d5105bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae795e0db135cf35cfab21b6e6b9e67935c4d63cd73caf86d89ae79ae7d5105bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae792e29f6bee46317ca7dc6cc3ee0e178e60d533bf376bb97b86d3d9aa07e56ab4b3dad2ef6b4ca419d4b40bf4b19f4cb405cda36ad53bc72992f12c0cc14bb60fb9749d07e8ab1ded3c3c6bf8ca9ed717dfd65e33c765c5f3fde63c7f5f5e33db6e6b9e67935c4d63cd73caf86d89ae79ae7526263b93e18be6ea7e79fda6d3cd195ebdc3ab2929fea5c39a1f83a25d0638823b61e437aaea886d89ae79ae7d5105bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae795e0db135cf35cfab21b6e6b9bc3ccfc1fb3563c013783c41099ebdc278160ae3992d8c67aa309e75c2782609e3592a8ca75618cf0e613c5b85f1f408e3e914c65310c6b34818cf32613c7384f14c13c6b34118cf64613c75c278f2c278160be3992b8c672c7ecf500ecf74613ccb85f16485f16c14c6532f8c6795309e9dc278fa84f16c13c6b344184faf309e2e613cedc278360be36915c6334f18cf0c613ce709e36914c6d3208c67a5309eddc278d608e3992f8c67a6309e9c309e26613c1384f1ec11c6b34b18cf5a613cdb85f16c11c6d32d8c6793309e0e613c2b84f12c10c6334b18cf14613ccdc278260ae3c908e0c906673f932c0befef035f8df7597bbd3467c6f0fb973b7f0d7ce60a57ae8dd8f6e5e0a3df865f11f159d4e972684bbf2be71fdb12ea84b1fa619de23502c7154278260ae36916c6334518cf2c613c0b84f1ac10c6d3218c6793309e6e613c5b84f16c17c6b35618cf2e613c7b84f14c10c6d3248c27278c67a6309ef9c278d608e3d92d8c67a5309e06613c8dc278ce13c6334318cf3c613cadc278360be36917c6d3258ca75718cf12613cdb84f1f409e3d9298c6795309e7a613c1b85f16485f12c17c6335d18cf3e613c7385f12c16c69317c653278c67b2309e0dc278a609e399238c6799309e45c2780ac2783a85f1f408e3d92a8c6787309e5a613c4b85f14c12c6b34e18cf54613cb385f12c14c6b357184f4d040fc3ffbf0c79e8fe35da36adef13129b613f84fff7f34aa6365de5b655efb64bfc14af0eea1c741da9bddf0b3f4b5cfefd86f8ddfc2ad0e82aa6b6d0fec878fb873b36de57190043e0e91344f070dc8fcad4ce117998e0ff9fcd5badaef6b4f2f75d0eea5c09fa5dcda05f546ed3fad5c043e77162cd42bd754218c97719334f16da4c4ba963e06ae0e1382699da19e6ea355e9bd645e84e753057af616867d4b143ebd700cf065726d62cd4db2084917c5731f364a1cdb494cad56b8087e3d8616a6798abd77a6dda10a13bd5c15cbd96a19d51c70ead5f0b3c1b5d9958b3506fa31046f25dcdcbd3918536d3522a57af051e8e6387a99d61ae5ee7b5696384ee540773f53a8676461d3bb47e1dec076556e62866cb43bf2320d62cd4db2484917cd7b0f274e4b3d0665a4af563d7010f473fcfa47bd88f5defb5695384ee540773f57a8676461d3bb47e7d44ec9620592d6e1885163744f0dc30c65a50bc7299af4c21b3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3049d2d0f3dc79358b3506fb31046f25dcbcb13fe2e68733072c978ebfd50be0178ae63d087a99de13de4fbbd366d8ed09deae0f1b59fa19d51c70eadef87fdb0bf0ce6eb53c8ac3a57c66c79e8ff67106b16ea6d11c248beeb7879c27e6c4b307229d58fed071e8e7e9ea99d613f76c06bd39608dda90e1e5f0718da1975ecd03ac55366658e63b63cf45c3662cd42bdad4218c977032b4f21fc7de3d660e452aa1f3b003cfb13e729f6630cba87fdd841af4d5b2374a73a98ab0719da1975ecd0fa41d80fe5305f9f4266d559758e63569d55e73866d559758e63569d55e73866d559758e63569d55e73866d559758e63569d55e73866d559758e63569dab4767cb43ffff9258b3506f9b1046f2ed67e5690fe71db60523978cb7de0fe583c07320719ee2bc0383eee1bcc321af4ddb2274a73a787c1d626867d4b143eb87603f8c77e6eb53c8acb93136cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc1272c3f26c776562cd42bded4218c9778097277ceec1f660e452eabe9d43c07390411fa67686f7ed1cf6dab43d4277aa83c7d7618676461d3bb47e18f683322b7314b3e5d9e1cac49a857a3b843092ef202f4fd88fed08462ea5fab1c3c0c3d1cf33b533ecc706bc36ed88d09dea60ae0e30b433ead8a1f501d80fcaaccc51cc9667a72b136b16eaed14c248be43bc3c613fb63318b994eac7068087a39f676a67d88f1df1dab4334277aa83b97a84a19d51c70ead1f81fda0ccca1cc56c7976b932b166a1de2e218ce43bcccc938536d352aa1f3b023c1cfd3c533bc37eec46af4dbb2274a73a98ab3732b433ead8a1f51b8167b72b136b16eaed16c248be01669e2cb4999652b97a23f0701c3b4ced0c73f5a8d7a6dd11ba531dccd5a30ced8c3a7668fd28f0ec716562cd42bd3d4218c9778499270b6da6a554ae1e051e8e6387a99d61ae1ef3dab4274277aa83b97a8ca19d51c70ead1f039ebdae4cac59a8b7570823f9b01fdbcbc4d3e8f1344668311e633779b19baa2476b317bbb94a626b9e6b9e57436ccd73cdf3a00a626b9e6b9e5743ec6acd35d5bc3a35cf9c43cd33e750f38c6a2e52f347938bdd990b86971a88c5303ed7369a3147e459288c679f309ed9c278a60ae399248c67a9309e5a613c3dc2783a85f11484f12c12c6b34c18cf1c613cd384f14c16c653278c272f8c67b1309eb9c278a60be3592e8c272b8ca75e18cf2a613c7dc2789608e3e915c6d3258ca75d184fab309e79c2786608e3394f184fa3309e06613c2b85f1ac11c6335f18cf4c613c39613c4dc2782608e3592b8ca75b184f87309e15c2781608e399258c678a309e66613c1385f16404f06483b37fdb81bf27a8051fdddfbf177c4f72e57de0ab898841db39063e1a3fa56dd8f3d58533ce66a881cfdc14c1f5a4887814e7a688cf8e85ee18ab1fd6295e2370dc248467a2309e66613c5384f1cc12c6b34018cf0a613c1dc278ba85f1ac15c63341184f93309e9c309e99c278e60be359238c67a5309e06613c8dc278ce13c6334318cf3c613cadc278da85f17409e3e915c6b344184f9f309e55c278ea85f16485f12c17c6335d18cf5c613c8b85f1e485f1d409e3992c8c679a309e39c2789609e359248ca7208ca753184f8f309e5a613c4b85f14c12c6335518cf6c613cfb84f12c14c65313c1b38f8927ee790afb04c4b6e3f0746d4a637459787f2c7e17b4cf63a4f5a3c0483ebc2f36cfc413f70c8abc80d8568b5550b64b16dec7df7570e554de63a4f5a89cc2fb1a5731f1c43db7639580d8560b9aaba07b00b2f03edec7cc9553ab3c465a8fcaa9665e9ef0ff07ac0c462ea5ee35c2638e631f32b5338fc75f82cfd0887c46f24a4fab1cd4198be748c4f507144f9995398ed9f2d05c13b1e2f96c2c7e47351ac6a8f32b034fd83fb606239752fde351e0e1387f30b533ecc78e7b6d6a8dd09dea60ae1e676867d4b143ebc72362b704c96a7162145a9c88e03931c65a50bc7299f7a5905982ce9687ee4524d62cd45b2184917c795e9eb07f5c118c5c4af58f278087e3fcc1d4ceb04f38e9b5694584ee54078faf930ced8c3a7668fd24ec8772988fa7905975ae8cd9f2d09c08b166a15e410823f98eb2f214f25968332da5fab193c0c3d1cf33e91ef663835e9b0a11ba531d3cbe0619da1975ecd0fa20ec076556666556666556666556666556666556666556666556666556666556666596cd6c79e8b7c3c49a857aed4218c9778295a738efd01e8c5c4acd3b0c02cfc9c4798af30e0cba87f30ea7bc36b547e84e7530574f31b433ead8a1f553b01f945999955999955999955999955999955999955999955999955999955999955936b3e5a167b6136b16ea75086124df495e9ef0775b1dc1c8a5d4bcc329e0e19897616a6738ef70b3d7a68e08dda90ee6eacd0ced8c3a7668fd66d80fcaaccc51cc96879e1d47ac59a8d72984917c83ac3cc5f9d3ce60e452aa1fbb197838fa7926ddc37eecb4d7a6ce08dda90ee6ea698676461d3bb47e1af64339ccc753c8ac3aabce71ccaab3ea1cc7ac3aabce71ccaab3ea1cc7ac3aabce71ccaab3ea1cc7ac3aabce71ccaab3ea1cc7ac3aabce71ccaa73f5e86c79e87f2c126b16ea75096124df29569ef670dea12b18b9949a77380d3c1cf3324cba87f30e67bc367545e84e75f0f83ac3d0cea86387d6cfc07e18efccc753c8acb93136cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc1272c3f274bb32b166a15eb71046f2ddcccb133ef7a03b18b994ba6fe70cf09c66d087a99de17d3b435e9bba2374a73a787c0d31b433ead8a1f521d80fcaaccc51cc96a7c79589350bf57a843092ef34334f16da4c4ba97e6c087838fa79a67686fdd82d5e9b7a2274a73a98abb730b433ead8a1f55b80a7d79589350bf57a8530920fcf71bd4c3c8d1e4f638416e72ab6d5a2cf95cf73af5978bf0f18b9fa965e8f91d631c7c9d7083c7d4c3c4d1e4f538416e72ab6d5620d94ed9285f7d70023574ef5798cb41e95534dc0b38689a7d9e3698ed0e25cc5b65aac75e529ee350befaf0546ae9c5ae331d27a544e3503cf5a269eb83e69ed18c48e3bbec622765cae8c456cd55c3557cd55734ecd33e750f3cc39d43ca39a8bd29ce13a2a1c3ba5180130e0d20f65fcaec071edc9d4ce7cd4f7b1b55e9bf0fb188e399cabef1bcaaccc71cc4ce3161d592f36e913783cb40c316b319663907d5e9ba48d4196cb7c3c85ccaa7365cc36f6adc9c7eec87ab1499fc0e3a1e556662d98da19f607b705d11a53bc1cd4c13cbd8da19d19884bdba6f5db603f94c37c3c85ccaa7365cc36f6ed89c72e3e9b1d63933e81c743cbedcc5af0b4b3d81fdc11446b4cf1725007f3f40e867666202e6d9bd6ef80fda0cccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaacccb2996dec27271ebb387e8fb1499fc0e3a1e5c9cc5af0b4b3387e7f6710ad31c5cb411ddce77732b433037169dbb47e27ec076556666556666556666556666556666556666556666556666556666556666596cd6c633f25f9d8e1ef713036e913783cb43c85590ba67686e3f77705d11a53bc1cd4c17d7e17433b331097b64deb77c17e5066658e62b6b19f9a78ece27c1ec6267d028f8796a7326bc1d3ce627f707710ad31c5cb411ddce77733b433037169dbb47e37ec8772988fa7905975569de3985567d5398e5975569de3985567d5398e5975569de3985567d5398e5975569de3985567d5398e5975569de39855e7ead1d9c6be27f1d8ede1f83dc6267d028f87967b98b5e0696771fcfede205a638a97833a98a7f732b433037169dbb44ef1aa81f9780a993537c68659734399e398353794398e59734399e398353794398e59734399e398353794398e59734399e398353794398e59734399e398353794398e59734399e398353794398e59734399e398353794398e59426ed8d84f4b3e76f87b768c4dfa041e0f2d4f63d682a99de1fd2ff705d11a53bc1cd4c13cbd8fa19d19884bdba6f5fb603f28b3324731dbd8f733c4ce7ab1499fc0e3a1e57e662d98da19f6070f04d11a53bc1cd4c17dfe00433b331097b64deb0f406cd43ba1d8e13d9014a3c6bd5adfd35db9167ccf70e53af03dd395ebc1f72c576e00dfb35d7902f89e03ed21df735d7939f89ee7ca6bc1f77c575e03be17b8721ff85ee8cabde07b912b0f81efc5ae7c0bf85ee2cab782efa5ae7c1bf85ee6cab783efe5ae7c07f85ee1ca4f06df2b5df94ef0bdca959f02be57bbf25de07b8d2b3f157caf75e5bbc1f73a57be077caf77e57bc1f706575e0cbe07237c6f74e5a781ef4dae7c1ff8deeccafbc0f716579e04beb7baf264f0bd0dcaf4fa76573e0f7c0fb97223f8dee1ca39f0bdd3959bc0f72e579e02be77bb7233f8dee3ca53c1f75e579e06bef7b9f274f0bddf956780ef03ae3c137c1f74e559e0fb902bcf06df875d790ef83ee2ca73c1f751579e07be8fb9f27cf07ddc951780ef13aebc107c9f74e545e0fb942be3fefdb42bdf0f3eea571e001ff52b4f071ff52bcf001ff52bcf041ff52bcf021ff52bcf061ff52bcf011fe5dd73c14779f73cf051de3d1f7c94772f001fe5dd0bc14779f722f051debd187c94772f011fe5dd4bc14779f732f051debd1c7c9477af001fe5dd2bc14779f72af051debd1a7c9477af011fe5dd6bc14779f73af051debd1e7c94776f001fe5dd83e0a3bc7b23f828efde04be16577e33f8ce77e5b780ef02577e2bf81ee7cad8cf5ce8ca6f07dfe35df921f0515ff80ef05de4caef04df12577e17f896baf2bbc1b7cc95df03bee5aefc5ef0ad70e5f7816fa52bbf1f7cadaefc01f0ad72e50f822fefca1f025f9b2b7f187c0557fe08f8da5df9a3e0eb70e58f81afd3953f0ebe2e57fe04f8ba5df993e0eb71e54f818fcee3d4cfd8e3d91e83a40369647dd4e6d688b6906f22b4a53f48f69a8e62d1b669bd1d18691f14c69eb1305ac6368fd1f27432688679454ba9ef1f9dc0d3c1c0c3d4cef0fb4797d7a676af4d39a87311b4b38ba19d19884bdba6f52e88cdb1cf518b7ab7dd259e167550e73fdc49ce9e3b4be948dbb0f95b88684b2f735b68dbd42ff58e41ec6e2f76de8b8dfd312da58eaf6e60ee6160b6dbed4b7ebbe1f1b5da6d8b728ae2e4a14d6b4083a4da84b133ce280ef9eba05c3363b82ed5233de8fc45ec3697695f22bbffb94eef7339a8d31bd1fefe20d9f6f7793c7d1eb3dd27ff9c3eccc1703c8439d0eb71d07a1eb4eb8bd1ae17b4a33a78fe6b63d2aec7e3a1f536e0a16b9c2ef0d1b502f1e37556eb1870fbfd5e570437f9ba81b12d82319f3c6378add3e631d27a1e18c9d7033cdd4c9af9fb7a89a70f9e971bbc3af4d93aa833cbf525538291df3fa8ae3dee166786db45dfc11f0d92edd31b18f4c2f18100f4093c0d03d08bda59cfc03339181e2338333478fac08d03970c1c389c01b43a0f135f3311cda8011f966b237c4130722804876469280487646b3c59700886eadbaf52b65934dc3070e2d8d0134f0e9c3c74fa8e53430387770fde88d4f51e3d92c6b50049d147cbc46078d0a63f4836791abc58a5926722bc4e60e0616a6778d29be4b5a9c16b530eead4c37b9318da9981b8b46d5ac70160f26176d2fb386150e3b505b37812bcfafb36d10651c0c7c1f6330ecebe57e7846d70f0242ef5187614d15ec9d91d6147056d6f6d47fdec616747f5ec8591edc8ed289d1d95b3a37076d4cd8eb2d951353b8a6647cdec28991d156b098aa35e1700cf5780d18e6ad9512c3b6a65bf49dab3821d85b2a34e7694c95e4dd86f29f60c6caf38edd9d09e05edd58efd3665bf59db338c3d7bdb33933d9bd8ab237b5564af62ed559d9d9959676cbdd37a83b18dc63619db6c6c8bb1adc6b619db6e6c87b19dc67619db6d6c8fb1bdc62e36f684a038a27c89b14b8d5d66ec89c62e377685b12b8d5d65ec6a63d718bbd6d875c6ae377683b1fdc60e183b68ec90b1c3c6068c1d3176a3b1a3c68e197b92b19b82e21d1e278c9d343668ec94b19b8d9d36762628ce12d959213b0b64677dec2c8f9dd5b1b33876d6c6ced2d859193b0b63675dee098ab32a7676c4ce86d891703bf26d47baedc8f63383e2c8b51da97e4e501c89b623cf76a4d98e2cdb91643b726c478aedc8b01d09b623bf76a4d78eecda915c3b726b476aedc8ac1d89b523af76a4d58eac3e1814474eed48a91d19b523a176e4d38e74da91cd8782e2c8a51da9b423937624d28e3cda91463bb2684712edc8a11d29b423837624d08efcd9913e3bb26747f2ecc89d1da9b323737624ee33c61e36f659639f33f679635f30f645635f32f6e5a098935f35f635635f37f60d63df34f62d63df36f61d63df35f63d63df37f603633f34f623633f36f688b19f18fba9b19f19fbb9b15f18fba5b15f19fbb5b1df18fbadb1df19fbbdb13f18fba3b13f19fbb3b1bf18fb6b303c6a8f1dc53fdd0a8d201f181a1a38716aa86568b0e5c42dc7878e9d3a7e47cb6dc7868eb60cde3a70fac8f1c1dbf0c36f775d130d8f6f387dfac01d2dc74e1e1eb8bd65f096a196c1232d07076f3979f80c7ee8cbee43f3cf8e78e0f0e1f8603fae790ca43fab30e89fdde768e26147e9b6fdad1241feb3920f4dabadac4157b8330b7d2bbdb47815d772e6f8e0504bbee5a4f97be0b8f9ccc0e1d6167cef8c11f9cc50cb99a103a7875a8e9c1e3cd1d2d68adb3d30a98246fc637a051f9a3963f42d0ffe1f68e5b57eb1020400", + "bytecode": "0x1f8b08000000000000ffed9df77f1cd5d5c667d55cd65acbbd0202e322abad569265b9ca40006363834d31cdb8c960b02d638b964220150209bca9d4407a25bd92de437a4842494823fd87fc0bf9bce7cedef3ead1f51dbd5a31573963ce7c3ec773e7f1ddb9dffbcc99bbab3bb3b3b9a8bcfc932267cbd5148ba2e317feff3ebb2ebeb8a523c57d154372e632c2599511ceea8c70d66484b336239c7519e19c9011ce8919e19c9411cec919e1cc6784734a4638eb33c259c808e7d48c70366484735a4638a767847346463867668473564638676784734e4638e76684735e4638e7678473414638176684f3a48c709e9c11ce5332c2d99811ce5333c2795a8a9ccdc0c973e1a7dbf562bb5e62d74bed7a995d37d9f572dbc71abb6df6d942d14ad1e6fc5fbb61a63093e225e7ff3a29ba28ba2956d8ff6bb4ffd743b192a2976215c56a8a35146b29d651acb79e6ca03883e24c8ab3285e467136c53914e7526ca4388f6213c5668af329b6506ca5b880e2428a6d14db292ea2b898e21287e5528a1d1497515c4e7105c595145751eca4b89a6217c56e8a3d147b29f651f453eca7b886e25a8a0314d7515c4f7190e210c5618a018a231437501ca5384631487123c54d8e673753dc42712bc5cb1dce5750bc92e25514b751bc9ae2768a3b285e43f15a8ad751bc9ee20d146fa4b893e22e8a3751dc4d710fc59b29de42712fc57d14ff43f1568ab751bc9de21d14efa47817c5fd140f503c6859f8447888e2618a4728de4df128c56314efa1782fc5fb28de4ff1018a0f527c88e2c3141fa1f828c5c7283e4ef138c527283e49f1298a4f537c86e2b3149fa3f83cc51728be48f1258a2f533c41f1158aaf527c8de2eb14dfa0f826c5b728be4df11d8aef527c8fe2fb143fa0f821c593143f723cff31c54f287e4af133abfddcae7f61d7bfb4eba7ecfa5776fd6bbbfe8d5d3f6dd7cfd8f5b376fd9c5dffd6ae7f67d7cfdbf5efedfa0f76fd47bbfe935dffd9ae5fb0ebbfd8f55fedfa6f76fd77bbfe875d9beb6333a695cb13a3a1a52f4a69cce9eaef35f3e36ca67bfdcd5cdba9b6ffc7eb46abd7d86d5ee7ac5e6bb76b1dbdce6ed739fb9968b7273a7ac16e171cbdc16e3738fa74bb3dddd167daed998e7eaadd3e15f4bcfdbf72c7ca2ba3555b29071ae75f1568b556ab06ad8e7707da04abd582c6c7b70eb449569b00da64ab4d042d6fb549a04db1da64d0eaad9607ad60b529a04db55a3d680d562b806653339a0ada74ab358036c36ad3409b69b5e9a0cdb2da0cd0665b6d266873ac360bb4b9569b0dda3cabcd016dbed5e682b6c06af3405b68b5f9a09d64b505a09d6cb585a09d62b593406bb4dac9a09d6ab553403bcd6a8da0f179792a68a75bed34ab999ce2712e7e8dd5ab403b9dcf65d016f3790cda123e87415bcae72f68cba06dd69af87c066db9d5389fcdffadb0e5be28adb1acb4c7ecb727edfdd29ecd7e7bd3df6fd18c8daba221affba09d1ef06ab52da7784f4007b69db3c1edb05e03e5b3a12ed7633f784c627633ceacb4e5d523bc6e85f3ba02d459e9e97f5f946eff7b1d9e5e87b916ca817276bfe6eca8978a73f622a8ebe61ebf3f9e8839bb1138d2cfd9ce4ecdd9512f15e7ec1ea8ebe61e7f563b1173f652e00890b33d6172b654d49c2dcf8f44913ff7f8ef86133167fb8123fd9cedd69c1dfd5271cede0675dddce3bf614fc49c3d061ce9e76c4f8f7e3618f55271cede0375dddce3f994133167ef008e0039bb47c7d9512f15e7ec8350d7cd3d9edb3b1173f65ee0483f677b03e56ca7e66c54bed61545fedce379e61331671f018ef47376afcecf8e7ea93867bf0075dddce36b1e2762ce7ecc96cd75869fdbeb0c0b41fb85d54e02ed97563b19b4a7ac760a68bf826b83acfd9aaf1b82f61bab9d06dad3ce3555a33d63b5d3417bd66a8b417bce6a4b40fbadd59682f63bab2d03ed79ab3581f67bab2d07ed0f566b06ed8f566b01ed4f566b05edcf566b03ed05abb583f617ab1541fbabd53a40fb9bd54aa0fddd6a9da0fdc36a5da0fdd36add5633d724f9dad7f7ad3611fade17a597a3f1f5ae68f89273b6fba0bc3c2c4fb1100dbf0f88db6a49bfad4ed3f7e668f47d6f019ed6007dcf431ba3e169059eb6f479e2efc9b5a7bfdff818373b9ee6a1ad66e8573140bf72d016ef9bb7b9bd0268386e143d8c1de93396f05e27de376f7700236b388ef138cce78f798ff917f0063897e2cf19dc1e7fff97395a40e73adf6a1862fbb7d5eae1ff714c6f73b4407919e705b7c5fbe6ed7660e4feb48d3f6369b48cad0e63a83122076df1beddb6f17c5f3efe9e8deab81640fb2f8c49a5b18e49f5c0d6048c4d817c4c3ad652da0ef17e958336786c63cfdb41e73acf5595d7666c9b9c1b620b70fe952afdfc86e341fa795c2ae2793d1a9e22f08438f7039daf457cdfff4f946eae951cafda1caf0a50a703fc2b05f06fa4cf21dc9e322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb332cb67c6fb2ff0fa26d76b12c2c85a3bf08498e78fbf4bcefbb26b735de721b8ae93fe758b5211af59f27d7b4b9c3ed7409dc77343751fb565dfb541bca6d914d6bb51dd67511f1d7f2d36e435c4a4ebc0beeb978da9b55dda1bea7a9b79ce85792641b3e36b93c7d300f7a90cf334e7788af7c72d73784c9e3e5f35c416e2da5fa5d722d12b2ea7796d0fef31087b5ccae307e74255347cfcc0f799ced4db1e7e0d93af97773a6dd7409dafe4868e0ddf03c9bf75e0def364ea7439fbe6d72c01bdcbd977837d2d73d43afb6f86d7729dafc398ba1b7235c4fd1f382e47d0575cfaa08cd7cdd37f1f2e5fc72f56c053029e10e34ca0cf1b453c07d2be8edfe578e5fb1cc3753ac1bfae00fef93e8bf236b7a7cccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaacccf299f1bba2cc9a877aed4218c7e9de87f87a063f7784f76faeeb6cad1a6a37f47538bee6b4d4e9730dd4b9af6aa8ee765bae8f8ebfdf21e95806b89e37e2b1e4f6eaa13f782d28d4f7b93b1c9e0e8f175c6e4cadedf275fcf43d1eba8e5f747c6df7781aea7cc56bace8299eaf6d0e0f5e1bad8f8ebfb7240ffb198f7b8792f282dbc373a903342ee3f7a3431c677c2f71efebe1f6f0faf5f5d6db8628d4b12f15438e1bddd0a7bee8f8fcae813a4760ec3b6acb780f07de3b7287e7ff7919e93a35fb17e8396ef1f15d099c7dd00eb6dd0bac29b5dd816de76c703bacd740f9f6aaa1baee73cad86b6637e788fb2c35dfebda9cd715a0ce0a4ffffba274fbdfe3f0f438cce698dc08797607bcff871a93562478b4043ce23af83928d43d79ee18e9dedf88f7edd53975f0330bd7b90bc6a8a4fb477df71c867a1f4bbae7d0f7d9b8048c6e1fddfb3c5feaf7693d00e345daf7693d003984f76945cefe97c2fe99ab2e4a7e6fe13a8f38fb773f93f36bf03e30aef3188c174fc1671df7f337de33351e7f5f25dd27cdede1e71a3cb7ffbfbe1be66ea8df972233e604b2e0e704aef351e798752570b7785efb78c26bd92b7e0e15fefde2fa677c48fd19b2f69cef71fac279bd02fac2753eeb7c060cf59ceff4fb3afc33098f031d9ebe729d2fc1b9f6047cc6e3e3847f773ce9f97f5e46fa0c88cfbd5d957e9fe3e38bcf77ed8376b0ed35c09a52db1dd8367f06e47658af81f20f614ce77aec077bcdece61ce1cf51c8eebeaed3795d01eaf47afadf17a5dbff550ecf2a87d91c93af419e3d099f01438dd5bd091e2d058fb80e7e3788df47f019b2bef79850f7f627bdc7f89e4189e3f35341e7d5fc6ceebc9aef3302d7e1d7e2678467609ccd7beabaf385fc7e99e67dc3f85d896668f7794fbb7d29fbd90abef5c1367e2ef86fb61de0b9a5f17744f0b982cc804b1f9499a100afabca00637506186b32c0589b01c6ba0c304ec800e3c40c304eca00e3fffddeb760c67c0618a76480b13e038c850c304ecd0063430618a76580717a06186764807166061867658071760618e76480716e0618e76580717e0618176480716106184fca00e3c919603c25038c8d19603c35038ca76580719132a6c2b82c2c6369ac8c8627d4ef1356f29b78e3f1fb842d9eb6025c172955da77bc5612e2f96995fe5e223314a2e37fff2b106369ac8c86a72d7d9e8a7f5fb30d3cf3fd0e6600c617f51b52929edb86f7a72f0fcb587a31cf960b715fe7589f1f86f7182c0fcbf8a29e7116e21e824a9f71e6bbe7617958c6d2581943ddcf8cdfe7180d0f7e3faec3e35900c6d25819437d0f02bfab351a9e4ef0ace4f12c006369ac8ca1ee99aaf49e3ebcb7bbd3e3992446e449fbf9a29d9eb6ba05f49d19b2c63829038c9333c098cf00e3940c30d66780b19001c6a919606cc800e3b40c304ecf00e38c0c30cecc00e3ac0c30cece00e39c0c30cecd00e3bc0c30cecf00e3820c302ecc00a3fe5da88cff6d46e4694c8f67d8fc07b615e27bad95f67d858727c5ef7d0eeb3bb615e0192715f71d9f059225c6ee0c30067e9e8dfaa88ccaa88c7a5eab8fe219d547f5511995511995f1a5ce9885315c1933918fa5b1321a9e95813ceba980672578c6af5b9e01c6151960ec0ecb581a2ba3e109f18ce13cb4311a9e5ef08c5fb73c2c6369ac8c8627c0f31063cf7a2be0c1e706f67a3c0bc0581a2ba3e1591dc8b35515f0ac06cf56793c0bc0581a2ba3e109f1ecc73cb4311a9e35e0d96a8f6701184b6365343c6bd3e7893d5b5301cf5af06c8dc7b3008ca5b1321a9e75e9f3c49eadad80671d78b6d6e35900c6d258190dcffaf47962cfd655c0b31e3c5be7f12c006369ac8c13414f9127f66c7d053c7de0d97a8f675219bb32c0d89d01c62cf8a88cca288951cf6bf55112a3faa83e2aa3322a637619576480518fb5324a65dc903e63a952c60dc0d3973e4f47a07ec6bfe37586dd578acf962819afce74bc5aef7855803a67807f6706f02f07edf2be799bdbab94f97401cc81da2ef1ef4baf77daeb76fc30cb59765d0b7a1778fa325be6dff0e3faf87b6e5ce712fb8329fcdba0bc54417fcf4abfbf1d239ddbdc1ef2ac11c6b340184faf309e59c278a60ae399248c6789309e6a613c0b85f12c15c6335b184f49184f83309e76613c9385f1340be3a911c6b34118cf22613c6b85f1ac12c63347184f8f309e69c2789609e3c90be3a915c6b35818cf5c613cd385f11485f14c11c6d3248ca74e184fab309e75c278560be399278c67a5309e19c2783a85f1d40be399208c67be309e99c2783a84f11484f1b409e399288ca745184f4e004f3e3afe9e853cfcff06d0aa9cd79af1b366dad0ff9f6df52a78cd39b65cedd9f7d9a0f1b5dd733caf459fce86bef4d972f1c52db14fd8561f6c737bf5c0718e109e16613c1385f1b409e32908e3e910c6335318cf7c613c1384f1d40be3e914c6334318cf4a613cf384f1ac16c6b34e184fab309e3a613c4dc278a608e3290ae3992e8c67ae309ec5c2786a85f1e485f12c13c6334d184f8f309e39c2785609e3592b8c6791309e0dc2786a84f1340be3992c8ca75d184f83309e92309ed9c278960ae359288ca75a18cf12613c9384f14c15c6334b184faf309e05c278d608e3a9f2f06c08c4c3d76f79dfbcbd4148db018e43fcbdf87303f569a3dd57addd2ff3737b3550e7b1baf2da7c7ec2d732977bbd1dff76d8081e6d0cd4173e1e39e7f8046ebb0bef2b88802172fc893c3c21eec708d4cf617998e2f3198ac6abf31cafdc6357803ae7827fe705f0cf97dbbccded29b3322731e3ef5b306b1eea750a6164edaca03c5dc54a7f83e33ce009f15e11c8f7781cdbe4f4a9d3e33bd7c15cdd14a09fbe7387b73779da6e8cd2f562f328bcd8ece1d93cce5e707b95329f9b4166f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466093e1b1e7ea606b3e6a15e8f1046d63686e589bf6fd4130d5f72ce761f943703cf7901fc09d4cff81ef2f39d3ef5787ce73a787e9d1fa09fbe7387b7cf87e35009f3a60c32abcf6363363cfc6c4466cd43bd954218593b2f2c4f3c8ead8c862f238d63e7034f88713e503fe3716c8bd3a7951edfb90e9e5f5b02f4d377eef0f616380ecaaccc3e66c3c3cf6860d63cd4eb15c2c8dae6a03ca5f8fb8dbdd1f065a4716c0bf08418e703f91e8f635b9d3ef57a7ce73a98ab5b03f4d377eef0f656380e95306fca20b3faac3e2731abcfea7312b3faac3e2731abcfea7312b3faac3e2731abcfea7312b3faac3e2731abcfea7312b3faac3e2731abcf2f1d9f0d0fff7608b3e6a1de2a218cac9d1f94a733beeeb02a1abe8c74dd612bf06c499da77cdd2180eff175870b9c3eadf2f8ce75f0fcba20403f7de70e6f5f00c7e14467de944166cd8df161d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e6296901b8667b52d336b1eeaad16c2c8da96b03cf1730f5647c39791eedbb90078b606f027503fe3fb762e74fab4dae33bd7c1f3ebc200fdf49d3bbc7d211c076556661fb3e15963cbcc9a877a6b8430b2b6352c4f3c8ead89862f238d6317024f88713e503fe3716c9bd3a7351edfb90ee6eab600fdf49d3bbcbd0d8e83322bb38fd9f0acb56566cd43bdb5421859bb202c4f3c8ead8d862f238d63db8027c4381fa89ff138b6dde9d35a8fef5c0773757b807eface1ddede0ec7419995d9c76c78d6d932b3e6a1de3a218cace1e71f66fb4f7a3cdd0560a982b6428de191d3775e305f78d9208c6781309e59c278a60ae399248c6789309e6a613c0b85f12c15c6335b184f49184f83309e76613c9385f1340be3a911c6b34818cf1c613cd384f12c13c69317c6532b8c67b1309eb9c278a60be3290ae399228ca749184f9d309e56613cf384f1cc10c6532f8c6782309ef9c278660ae3e910c65310c6d3268c67a2309e16613c39013cf9e8f8eb9c78fda41a34be9eb10eb48b6c790368559e36783fdb41e3bfcf791f663c9e3aedf8f6aa3ced6df7708d879fd8561f6c737bf5c0b15d084f8b309e89c278da84f11484f17408e399298c67be309e09c278ea85f1cc10c6334f184fab309e3a613c4dc278a608e3290ae3992e8c67ae309ec5c2786a85f1e485f12c13c6334d18cf1c613c8b84f1d408e36916c63359184fbb309e06613c25613cb385f12c15c6b350184fb5309e25c2782609e3992a8c6796309e05c2783608e3a9f2f05c9c3e4ffc9d32ee7b045cb8f441f9e2c0fe04ea67d1ecf712bbaf14bfcb127f57ed52c7ab0d8e5705a87309f8776900ff72d02eef9bb7b93d6556e62466c3c373ab78bd94ebb50a61646d5b589e787c6c8d862f238d8f97024f8071ac23503fe3716c87d3a7568fef5c07737547807eface1ddedee169bb314ad78bcb46e1c5651e9ecbc6d90b6eaf52e64b32c82cc167c3c3f72a306b1eeab5086164ede2b03cf1f8d8120d5f461a1f2f039e10ef1f81fa198f09973b7d6af1f8ce75f0fcba3c403f7de70e6f5f0ec7a112e61d1964569fc7c66c78784e9b59f350af5908236b9706e529c5bf51d91c0d5f461ac72e079e10e37c20dfe371ec0aa74fcd1edfb90e9e5f5704e8a7efdce1ed2be03828b3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb36c66c3c3df4d63d63cd46b12c2c8da654179cad71d9aa2e1cb48d71dae009e10d76502f91e5f77b8d2e95393c777ae83b97a65807eface1ddebe128e83322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb332cb66363c6db6ccac79a8d7268491b5cbc3f2c4dfdb6a8b862f235d77b81278425c9709d4cff8bac3554e9fda3cbe731dccd5ab02f4d377eef0f655701c9459997dcc86879fb5c5ac79a8d72e8491b52b82f294af9fb647c39791c6b1ab8027c4381fc8f7781cdbe9f4a9dde33bd7c15cdd19a09fbe7387b777c271a88479470699d567f53989597d569f9398d567f53989597d569f9398d567f53989597d569f9398d567f53989597d569f9398d567f53989597d7ee9f86c78f837b398350ff58a421859bb32284f677cdda1180d5f46baeeb01378425c9709e47b7cdde16aa74f458fef5c07cfafab03f4d377eef0f6d5701c4e74e61d1964d6dc181f66cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e6246609b961783ab83dbbce43bd0e218cac5d1596277eee4147347c19e9be9dab816767007f02f533be6f6797d3a70e8fef5c07cfaf5d01fae93b77787b171c875dcaaccc1e66c353b26566cd43bd921046d67686e589c7b152347c19691cdb053c21c6f940fd8cc7b1dd4e9f4a1edfb90ee6eaee00fdf49d3bbccded29b33227319bb6f7a4df763c1e60dbec4fe4f0f0b227b01781fa198f077b23bfc7dc5e01eae031df1ba09f396897f7cddb7be13854c2bc2383cceaf3d8984ddbfbd26f3b1e0fb06df627727878d917d88b40fd8cc783fec8ef31b757803a98a7fd01fa99837679dfbcdd0fc7a112e61d1964569fc7c66cdade9f7adbe5e7a461dbec4fe4f0f0b23fb01761fa591e0fae89fc1e737b05a883797a4d807ee6a05dde376f5f03c7419995599995599995599995599995599995599995599995599995599995599965339bb6af4dbdedf2fc3db6cdfe440e0f2fd706f6224c3fcbf3f70722bfc7dc5e01eae0313f10a09f396897f7cddb07e03828b3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb36c66d3f675e9b71d7f1f07db667f22878797eb027b11a89ff1fcfdf591df636eaf0075f0985f1fa09f396897f7cddbd7c371506665f6319bb60fa6de76f97a1eb6cdfe440e0f2f07037b11a69fe5f1e050e4f798db2b401d3ce68702f43307edf2be79fb101c874a98776490597d569f9398d567f53989597d569f9398d567f53989597d569f9398d567f53989597d569f9398d567f53989597d569f9398d5e7978ecfa6edc3a9b7dd19cfdf63dbec4fe4f0f07238b01761fa599ebf1f88fc1e737b05a883793a10a09f396897f7cddb03701c4e74e61d1964d6dc181f66cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e6246609b961da3e927edbf1f7d9b16df6277278783912d88b40fd8cef7fb921f27bcced15a00ee6e90d01fa99837679dfbc7d031c076556661f339e2713d36b3bbe1f8edba8b26ba31db5e56ad08ed9720d6883b65c0bda8db65c07da4db63c01b49ba13facdd62cbcb40bbd5964ba0bddc96af06ed15b6bc0bb457daf26ed05e65cb7b40bbcd96f782f66a5bde07daedb6dc0fda1db6bc1fb4d7d8f235a0bdd696af05ed75b67c00b4d7dbf275a0bdc196af07ed8db67c10b43b6df9106877d9f261d0de64cb03a0dd6dcb8b40bbc7a3bdd9962f01ed2d1eed5e5b9e04da7db63c19b4ff8132afdf6acb53407b9b2dd783f6765b2e80f60e5b9e0ada3b6db901b477d9f234d0eeb7e5e9a03d60cb33407bd0966782f6902dcf02ed615b9e0dda23b63c07b477dbf25cd01eb5e579a03d66cbf3417b8f2d2f00edbdb6bc10b4f7d9321ea3f7dbf211d0781cb801341e078e82c6e3c031d0781c18048dc7811b41e371e026d0781cb81934ce9d5b40e3dcb91534ce9d9783c6b9f30ad038775e091ae7ceab40e3dcb90d34ce9d5783c6b9733b689c3b7780c6b9f31ad038775e0b1ae7ceeb40e3dc793d689c3b6f008d73e78da071eedc091ae7ce5da071eebc0934ce9dbb413bc996ef01ed645b7e3368a7d8f25b406bb4e57b413bd596ef03ed345bc67181c79fb78276ba2dbf0db4c5b6fc76d096d8f23b405b6acbef048ddf73de055a932ddf0fda725b7e00b4665b7e10b4165b7e08b4565b7e18b4365b7e04b4765b7e3768455b7e14b40e5b7e0c347e9f7c0f689db6fc5ed0ba6cf97da075db328f0be6fc33e70cf799fda887fe357bb85933e73473f745e97e66e2b678dfbccded1946f69bff6f1c194ba3656c73180d4f2980679843bc8cf4b75f09783a02f004ea67fcb75fa7d3a7a2d3a702d4391dfad919a09f396897f7cddb9dd07688638e5ed4dafd2e76bca8813acfda3739b31ac947de87c9df764f5f5604ee0bef9bc7a515e3d07697d376abd3368ebdbc8c747e750173770066b3df9ef4f71b9f5f2bedbe38a7b89d56e8532f7890569fb0ed9c0d6e87f51a28ffbb61a82ed7633ff8fd8bd94d2ef3b14476f77525e77505a8b3c2d3ffbe28ddfef7383c3d0eb339262f340c7104381fe21c58e170f0762b78d793e0dd0af08eebe0fb5f7b20efba1d9e6ea76dc3c39f713a41e3cf0a3807c1ffdf3c0edceeb8d7e9e166ad0b187d9f75dad2671cf1b34e1b30b2d60d3c5d813c738ff562c71f7c5fae73eaf06b6ba04e95fd63af211afeb706d735e7ddbfa05ffc37f37fa274c7f4ba007ee1dff311f813391e46e017f7b33600cfe468e86ffa63830347775fd3bfad7ff7be1ca0d53898b8ce79ba51051a96ab3d5a140d9fbac0294f9ebac029cf2ac7169c32e1fae64f29d32d9e1ee83f7460f0e2c3fd87f71ebdf5c860ffbecd03d72075ad438fa4493d4052d47c3d1a897e5274fcc14ff5cc64b845b0ff9c05a9b69da8b540c6be89d1907d3cb363deadcd8862666acc19694e4e63ad996931332b6626c5cc9c9899123333626642cccc8799e930331b6626c3cc5c98998ac6a83c13711af07c1f18cd5f08e66c37330966e6c0cc14987709f3e9d38cace6938479d733239d19e1cca764f317931939cca86c461c334a98773df36e673e9d9877eb5514ab29d650aca55847b1de7abd81e20c8a3329cea27819c5d914e7509c4bb191e23c8a4d149b29cea7d842b195e2028a0b29b6516ca7b888e2e2a83cf3776954bec27919c5e51457505c497115c5cea83ca3be2b2acf989b197233236e66c0cd8cb799e13633da6606dbcc589b19eaeba3f20cb499713633cc6646d9cc269ad943335b686607cd6ca099fd33b37d6676cfcce699d93b335b6766e7cc6c9c997d33b36d6676edf6a83c7b6666cbccec98990d33b35f66b6cbcc6e99d92c337b6566abee8ecab35166f6c9cc3699d925339b64668fcc6c91991d32b34166f6c7ccf698d91d339b63666fcc6c8d999d31b33166f6c5ccb698d995c7a2f2ec89992d31b3236636e403141fa4f810c587293e42f1518a8f517c9ce2718a4f507c92e253149fa6f80cc567293e47f1798a2f507c91e24b145fa67882e22b145fa5f81ac5d729be41f14d8a6f517c9be23b14dfa5f85e54cec91f50fc90e2498a1f51fc98e227143fa5f819c5cf297e41f14b8aa7287e45f16b8adf503c4df10cc5b314cf51fc96e27714cf53fc9ee20f147fa4f813c59f295ea0f80bc55f29fe46f1778a7f50fc331a9a49c581e209bbc1b37abb0707fb0f1d196c1c1c683c74e3c1c103470ededa78f381c16b1b076eea3fbaffe0c0cdf8e2ebec30c453961b8e1edd7d6be381c3fbfa6f691cb871b071607fe39e811b0fef3b862fbad3be68e1f12deedeb72fb9b1fbab5e04e9c3636cf423f6753c19bc71e4be7d7c2c867c662c2f7a7a8c1dbad8be8bacb1dbdbcbefd48dc70e0e0c36161b0fd3bfbb0fd26bfaf7b535e2ff1d23938f0d361e1bdc7d74b071ffd181438d1d6db8df47ebc6d089671ac6f0a2dcb4d1f73cfa5f16540aa2d9140300", "isInternal": false } ], - "packedBytecode": "0x000000028df71de500000047751f8b08000000000000ffed9d777414c7dae65b2030782c01ced9c2e13a60632144163038e78c8d31c62004061b104118e76c72744ee49c93c91803c6d8d737e7ec74af6fd87376bf3dbb7fecf79dddf56ed74cbd9f1e8aea4123770d8f34d5e794a6fa5575bfbf7afaedea54dd5510a4a77f85a940e79b86e9c2e0c849fe9fd4bfa5df6fea10e3ba4a5d72163410ce260d84b36903e12c6c209ccd1a0867f306c2795c03e16cd140385bc6c859007cae788f6f60bc8906c67b42d030e2b6a88170163710ce560d84b37503e16cd340384f6c209c273510ce931b08e7290d84f3d406c2795a03e13cbd81709ed14038cf6c209c673510ceb31b08e7390d84f3dc06c2795e03e12c69209c6d1b08e7f90d84f3821839db01a7dccbbf48fffe40ff5eac7f2fd1bf97eadfcbf46f3b5dc7423d7f7998ae0853fb305d69fc4f09a36ee89785a9a3f1bff230750a53e73075d1ff2bd1ffeb1aa66e61ea1ea61e61aa0853cf30f50a536fad479f305d15a6abc3744d98ae0dd37561ba3e4c3784e9c630dd14a69bc3744b986e0dd36d61ba3d4c7784e9ce30dd15a6bbc3744f98fa86e9de30dd67b0f40bd3fd61ea1fa607c234204c0f866960981e0ad3a0300d0e5365988684a92a4c43c3342c4c0f876978984684e991303d1aa691611a15a6d161aa0ed398308d0dd3b8308d0f534d982684e9b1304d34347b3c4c4f84e9c9303d65703e1da667c2f46c989e0bd3f3617a214c2f86e9a530bd1ca657c234294c93c334254c53c3342d4cd3c334234c33c3342b4cb3c334274c73c3f46a985e0bd3eb617a234c6f86e9ad30bd1da677c2f46e98ded32cb223bc1fa679619a1fa605615a18a645615a1ca625615a1aa665615a1ea615615a19a655615a1da635615a1ba675615a1fa60d61da18a64d61da1ca62d61fa204c5bc3b42d4cdbc3b4234c3bc3b42b4cbbc3b4274c1f86696f983e0ad3be30ed0fd381307d1ca68361fa244c87c2f469983e0bd30fc3f479987e6468fee330fd244c3f0dd3cfb4ede7faf717baacecf3bfd4bfbfd2bfbfd6bfbfd1bfbf35caffce98ffbd31ff07fdfb47fdfb27fdfb67fdfb17fdfb85fefd52ff7ea57fbfd6bfdfe8dfbfeadfbfe9df6ff5efdff5ef3ff4ef3ff5af7afef75edb74be45503b258398daa4f2a183d5fd7f11db7cbea89e5d35d5ff93df126d2fd4f3f25ba0edcdf47c33c3de5ccf3737d6d342cfb730ecc57abed8b0b7d6f3ad0dfb897afe44c37eb29e3fd9b0b7d5f36dc19ed0ff4b572cfda36c4db5a9006c129f4dc0d64cdb9a82adb9ac0e6cc7695b33b0c9f66d0eb696da761cd88ed7b616604b685b4bd1324c27685b32882b564a07abf516c5bd5efdcca4387ede4ab5de568e785bc7cf3b44adb78d035e151f27ea751541dc9ca46dc5603b59db5a81ed146d6b0db653b5ad0dd84ed3b613c176bab69d04b633b4ed64b09da96da780ed2c6d3b156c676bdb69603b47db4e07dbb9da7606d8ced3b633c156a26d67814d37b9c1d9603b5fdbce01db05da762ed82ed4b6f3c026e778256093f3bdb6609373bff3c126e78117689b6a3b5a16803f6d97762be54fda6cb0fd40da6bb05d2c6d35d82e91761a6c97826fb15d066d8dd8da699bb45bea7fdd753e19c4b59f94a5f6931e71af375cb35a6fcff8d79b7a7ed72ba8d53a097e7a8056bd753ec6be2d1dd0b79cdb881fb11742fe26282be5440f39f608bb3a1654e87cef0ccb7537962b86321596fa278378ebdfd3e0e969303783bc9b98ed58ea63b6ce53d631db0fca9ab127e7418d31666f050e07315bee63b6ce53d6313b0cca9ab127e7c28d31661f000e0731dbd54dcc9695fa984ddf1b0b027becc9f550638cd911c0117fcc76f2315bf729eb987d11ca9ab127d7c48d3166270247fc31dba5ab3f37a8f39475ccce81b266ecc9fd99c618b3af008783981de2dbd93a4f59c7ec7c286bc69edc2b6c8c31fb1a70c41fb3dd1cc56c471fb341fa396710d8634fee5b37c6985d041cf1c7ec107f7fb6ee53d631bb0bca9ab127cf501a63cc6ed079f59ce1e7fa39c3d960fb85b69d03bcf1c7765599a3d8eee0633bddff2308ec312acff31a636c7fa4f32a8e7f05fd11c4f66be9ab00b6df68dbf960fbadb65d00f572b00f74f1fb40ddeb94ed3ef067286bc6b23c5b6e8cfbc02f80c341cc76f5315bf73a651bb3ff05ca9ab127fd1c1a63cc7e091c0e62b69b8fd9bad729db98fd77286bc6de253adf1863f6bfeabc3a5ff8a33e5fb80c6c7fd2b67660fbb3b65d0eb6bf68db1560fb42dbda83ed4b6dbb126c5f695b29d8bed6b60e60fb46dbcac0f6576deb08b6bf695b39d8bed5b64e60fbbbb67506db3fb4ad0bd8fea96d5dc1f62f6deba66daa9f9ef4bd3aa46d2d803f19c4bb6da5dfa5ac5be63be4c0772bc377ab1cfa6e63f86e63f15de6c077027cc85460cc27215fe696a7b41878d05779fcbe3aaaba770cea5ef772e0e9e4a0ee09f051179e4ec0d3397e9ed4f1b34bfceb4d6de38e86a609f0d511ead5d541bd0ac097ac5be6c55f31d8b06ded6a61ec163f635901f89275cb7c3760141bb6f5f22e95ec3fea787871412daf837d29754e24fee4db55c2510e7629d3a16d2d5b3bcd5604ffc7e35e67c3e6282e537121be64dd322ffe8aa03e9d73cf585657c64e06a3ab36a2007cc9babdefdaed20793c8ebbb8d6b1b569e2bb470e7c77337c971bbeb1ed9429d3b1ad1b30c77ecda98f6d15f1afb714af4fe4da50fce0f9035ec3c55527f42dd786e247ec8590bfada0b6ac94133da41d167615cbb22d91dd5caeabb15c3194e961a97f3288b7fe15064f85c1acb6c9d5702c74b03fa462a087c121f3e5a05d4584763d403b29730968e7aa3deb6ef0c87c67e09176ac0bf0b8ba268ae2c9c5f5d8d17ce3392c5e3fcbfff13cc0d5f6ea6030cabc6d7b750346dbb98a83eb998ce72a9d80516cdd81a7a323cda2b66b4712df0e6225d51e890f393797fdb733d8a54c37fd429d6a2b1f81b6d2458c603cca54d76bdef8b75359ea1abc53163cb8ed1c5c577570148fa578ffe6bb20de5833dba54e865651f7785cb5e51d0d1e99177f9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec99f999f17912f6db92729d4918cdfe6faeeef3a7be61a8d725eb57cf75feb7d3fe6065a5d83f46fa415c66d4b910ca1435a92dfbffa03f98f99c0afb487676ab5d6a5b627fcc24cc8b3fec6b85db92a1bf53496cbed3dfa274f1bc4df5b156dfd134fb7e76b668eaa2ff336a5a60688afdf12f3778549c5634ad6573f1ec2fdb6791a895e4e37cb6571cd8633dfeed5276d833eb26c1e1ed071e675cf5df91b65a9e97f7307c174299b39ad46e1be95b25e3cc763496c37e3fb26e59e632b05718eb6ead97158e66c6fabbc0b252e65c6853f737a9d5cc415b59966ddf757c6e1eff7138fd1cbf63163c1d80c7453be3e87ce3b0efadc6fd1cdfec9f663b8f9132d8b7cf41bfca8cfd9dc49f67f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b667e66c5633e6bc5f76bcb491873d4f721f53c43be5526eb4f7d2fb849ad5fd7cfe1e499533ba3cef8eee87f6f525b768bce170547f67788da96aebe4f11b52dc51f7e7b069f05b978ae5b00be64dd65162d245f129bef32676343c873fc8e86aee5164d5dedaff88c1535c5fdb593c183cf46a3beed5366d85cf61d8a8a0bf187fb5219d8248fef47bbd8ce782c31fbf5883f7c7efd23ad6debc0d5b62f2b75d96ee0775392c191f18ddf53f939b47dbfd479ecc3817d47beb6fc5fa64ccfa9453f556707dfd72c2d8075c9f6b57ddbb30fb0c6e4bb03aeab40a73e86068590ffaa496d59292765456b6157fb887c0306d9cde53a19cb1543995e96fa278378eb6f7e6bb5b7c1acb6c96f20cebe86e3bfab36a95784469781465206cf835cf5c933db48b37f23f6db6b6e94c1731629f32f68a3a2fa8fdafa1cba3a8e45f539b49d1b770046b38e663fcf7cefa7f51fd05ec4dd4feb3f2086b09f5660acbf1dac5fb89a07d1c71629f37f8df59be7e4b20cf603fbcffe4ff03d97329dcfe69cfc585d5fd9cec971b9a8ba2b66fc0e5a3246668c0964c1f3042973bcd65ab65945047737cbb24511cb8a56e6b7c28a8223f573f39db5f43edfdba88bc4357e835cca9c04757173de923e0774f54db924d449e5cb2c759532a7c3be76a6ce27603be17e7ba9e5ff32653a07c431dcaf8abfcea9ed7b357026c10ffabe065863f2dd017dcb39a0f8117b21e42f695a5b56ca891ea2b5b0ab7d44cea390dd5cae87b15c3194e963a97f3288b7fe57193c5719cc6a9b9c03717629f44377d556f789d0a81d682465f09ea2ed3ba0b6638cabbefd51c798326034db4d3c4eba6433df1332efabd9ce11a48c2c8be7085da09d4d58ca9af70be5781967bf617c57a20bf8c577255c7dbbb93be89684793c2f3896be5d7cc756f98b1a33a17b0e7c478d99900bdf6d0cdf6d72e8db6bee3567d2dcc11804a9f7cff09ba56aca745e8ae312c8724d80d1c5580e89e0f06f8f1f8d11c77790e59a02a38be343b6df3eef028cb25c2130ba78b714c7dfa80b237e63188ff3c2e8e05bb11deafbad58bca7d71c1899ded9c46753c701a38b7ba8f57d570fcfe75bc0afab71893a66c188e7f3b25c4b6074716f1c9f0dd68511af8b64b9e381d1c533ac6cc777c26fcfe3bd65978c998eed8efba294657befa5c22d4fc6730df4ed605cc39416789ff1685af474cb93f1dc077d3bb8ef97d202c7193c9a16f86cd0c5b88789e0f0e77047e3c1e797b2dc89c09874c4d83b0bc62430fee7bd6260ece388319905631f6014fbc9c0e8e0fe6b8ab14f168c789f52963b0518af76c47855168c5703a32c772a30bab8979a00bf7561bc061865b9d380f15a478cd764c1782d30ca72a703e3758e18afcd82f13a6094e5ce00c6eb1d315e9705e3f5c028cb9d098c373862bc3e0bc61b8051963b0b186f74c47843168c3702a32c773630dee488f1c62c186f024659ee1c60bcd911e34d5930de0c8cb2dcb9c0788b23c69bb360bc051865b9f380f156478cb764c1782b30ca7225c0789b23c65bb360bc0d1865b9b6c078bb23c6dbb260bc1d1865b9f381f10e478cb767c1780730ca721700e39d8e18efc882f14e6094e52e04c6bb1c31de9905e35dc028cb5d048c773b62bc2b0bc6bb815196fb0130dee388f1ee2c18ef01c6bb2d8c7d1d31de9305635f6094e52e07c67be3674c5d4bf7cd82f15ee0b92f7e9e9466f766c1739f5b9ed477f5eeb5f8ba3f7e5fa96dd12fa87bddef079efef1f3a4b6c5fd59f00843312c879a3d103f634ab3fe59303e003c03e2e74969f640163c0340b3072c9a3d183f634ab30159303e083c03e3e74969f660163c0341b3072d9a3d143f634ab38159303e043c83e2e74969f650163c83825acd1eb26836387ec6946683b2601c0c3c95f1f3a4341b9c054f256836d8a2d990f819539a5566c1380478aae2e7496936240b9e2ad06c8845b3a1f133a634abca827128f00c8b9f27a5d9d02c78868166432d9a3d1c3f634ab36159303e0c3cc3e3e74969f670163cc341b3872d9a8d889f31a5d9f02c184700cf23f1f3a4341b9105cf23a0d9088b668f3a627c240bc6472d3c717f27fb118baf518eea3e32a87bdd85a11896c37e12a31d318eca82713430ca72d84fa2da11e3e82c18ab8151964b3866ccd44fa21a7c8f89df77aa5daa0eeaaecf18b73c19fb49a0efb18eb41813d45d8bb16e7932f69340dfe31c693136a8bb16e38067bc032d12e0a32e3cc2500ccb613f891a478ce3b360ac0146590efb494c70c4589305e3046094e5b09fc4638e182764c1f81830ca72d84f62a223c6c7b2609c088cb21cf69378dc11e3c42c181f0746590efb493ce188f1f12c189f0046590efb493ce988f1892c189f0446590efb493ce588f1c92c189f0246590efb493ced88f1a92c189f0646590efb493ce388f1e92c189f0146590efb493ceb88f1992c189f0546590efb493ce788f1d92c189f0346590efb493cef88f1b92c189f0746590efb49bce088f1f92c185f004659ee51c78c99ae5f5e68e4bea3ae551abbefa8eb92c6eedbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe73ece997cbfe8c077027cc85460cc27212f0cc5b0dca39eb15133224f497c3ca55877f4f51241dd5fb2f01438aa3bfa7a99a0eec2d0d0185f6c008c8f360046af63ba0f627d1815cf2bf0ff648c3c2f67c1f30af04c72c4f34a163c93806772fc3ca9989a94058f3014c3728f3600c6171b00a3d7d1ebc8c4e875cc1f1d3da367f48c9ef15830368436dc333688782cab2fa3e299123f4f4ab3c959f04c01cd64b9fb1a00e34b0d80f145b78c65f565543c53e3e7496936250b9ea9a0992c779f5bc6b2fa322a9e69f1f3a4349b9a05cf34d06caa4533078c65f565543cd3e3e74969362d0b9ee9a0d9348b660e18cbeacba87866c4cf93d26c7a163c3340b3e916cd1c3096d59751f1cc8c9f27a5d98c2c78668266332c9a39602cab2fa3e299153f4f4ab39959f0cc02cd665a3473c058565f46c5333b7e9e9466b3b2e0990d9acdb268e680b1acbe8c8a674efc3c29cd6667c13307349b6dd18c95f1d106c0f862036074ac63597d1915cf5c473c73b2e0990b3caf3ae2999b05cfabc0f35afc3ca9987a350b1e612886e51e6d008c2f360046afa3d79189d1eb983f3a7a46cfe819b3637ca90130fa6ded1959191d5c5f657c17e9d546ee3bea5da4c6ee3bea5da4c6eedbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe73ecef3c1b78f731fe7f9e0dbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe73ecef3c1b78f731fe7f9e0dbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe73ece997cbf1ebfefb26cbf31f33af0b8f8e68da37a96aaf5bea1d7f55d8cfa29adde34b47ad5d0aa18cabc01fabde940bf02f02beb9679f1972df3c504cc8e7c97a9f6a525d45f7cbc68e8a1fcbfe5a8ee516dfd5b8ddc77545bdfd87d47b5f58dddb78f731fe7f9e0dbc7b98ff37cf0ede3dcc7398b6fcc370b6acfdbe5fba76a1d6feb7ca19e97f22f815dca3c755cfab775e0f72117befd3ee48f15f9e0dbc7b98ff37cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ece7d9ce7836f1fe77c715e0cff1f9b039ec0e00932f04c21e3a922e3e947c67333194f6f329eb9643c8f93f17425e31949c6f31019cf5d643c2f90f15c47c6f31c194f07329ef1643c53c9788691f1f427e3b9958ca70f19cf93643c5790f17426e3194dc633988ce71e329e52329e57c8786e20e3c9c5fb4bd9f0b427e3a920e39940c6338d8c673819cfa5643c03c8782e21e3b99d8ce76a329e62329e56643ce5643c4f93f18c21e31942c6732f19cf4d643cbdc878e690f14c24e3e946c6339d8ce711329e49643c03c978ee24e3b9968ce732329e67c978cac8789a92f18c23e3194ac6733f194f3b329e5bc87892643c4f90f17421e39941c6338a8c671019cfdd643c2f93f15c4fc6f33c194f0f329e1a329e87c9781e20e3b9988ce736329eabc8784e20e32922e3e944c6f31419cf4c329e6a329ec9643c95643c7dc9786e24e3e949c6339b8ce731329eee643c23c8781e24e3b9838ce71a329e2bc9789e21e3694dc6d3868ca72319cf2c329e02029e4470e4182609f8ffeb606b622cab3efb5ad3b6f6ffef687b1358e65d9d6f6a59f73b60936fc9be6b5916757a07ea92d4f9d2ef37a574425f4998177f45c0f12e09cf2c329e8e643c6dc8785a93f13c43c6732519cf35643c7790f13c48c633828ca73b19cf63643cb3c9787a92f1dc48c6d3978ca7928c6732194f3519cf4c329ea7c8783a91f11491f19c40c6731519cf6d643c1793f13c40c6f330194f0d194f0f329ee7c978ae27e379998ce76e329e41643ca3c8786690f17421e379828c2749c6730b194f3b329efbc9788692f18c23e3694ac65346c6f32c19cf65643cd792f1dc49c633908c671219cf23643cd3c978ba91f14c24e39943c6d38b8ce726329e7bc9788690f18c21e3799a8ca79c8ca715194f3119cfd5643cb793f15c42c633808c47bee7c9c2339c8c671a19cf04329e0a329ef6643caf93f1dc40c6f30a194f2919cf3d643c83c9784693f17426e3b9828ce749329e3e643cb792f1f427e31946c633958c673c194f07329ee7c878ae23e379818ce72e329e87c8784692f17425e3799c8c672e194f6f329e9bc978fa91f15491f14c21e3196be179dd118fbcef2eeb96f9d7497c3bd80ea56abdef39aad3fb7a5dcdf47a855ffc154299c9c7a77fd5fbd8b8ac7099df27c07b71ef8346ef3baa8b6c8f0263fba0efb71df996777e64dd32ff7623f7ddcaf0dd2a4f7cb7317cb7c913df3ece7d9ce7836f1fe73ecef3c1b78f731fe74cbe1d5c1b94e177d2642a30e69390c7eb0517df977354cfc3ae13bf8b513fa5d53c432bf3daaa18cabc07facd73a09fedda53e6c55fb6cc171330635c9404f1c6c5fcf8eb54a6daad96a0eb7c435facd702479a461d43163472df51c790c6ee3bea18d2d87dfb38f7719e0fbe7d9cfb38cf07df3ece7d9c33f95ea8f3315e3796a20ff57c51ae071682dfc53a5f10a35fb5ae457a5d857addc2b118ec52a64922fddb3af0fbbc0bdf7e9ff7c7b67cf0ede3dcc7793ef8f671eee33c1f7cfb38f7719e0fbe7d9cfb38cf07df3ecef9e2dccc4b7ff14b80cd557ffea858ccc5bb04c7d277542c3676df51b1d8d87dfb38f771cee47b8903df09f02153a63e7e4b806791031e47f54c3ddb586ad4e975a34ec550068ff14b1dd4b300fccaba657e29f0c83416785cc4415db639f24c21e3a922e3e947c67333194f6f329eb9643c8f93f17425e31949c6f31019cf5d643c2f90f15c47c6f31c194f07329ef1643c53c9788691f1f427e39947c6732b194f1f329e27c978ae20e3e94cc6339a8c673019cf3d643ca5643caf90f1dc40c6d39e8ca7828c670219cf34329ee1643c9792f10c20e3b99d8ce76a329e62329e56643ce5643c4f93f18c21e31942c6732f19cf4d643cbdc878e690f14c24e3e946c6339d8ce711329e49643c03c978ee24e3b9968ce732329e67c978cac8789a92f18c23e3194ac6733f194f3b329e5bc87892643c4f90f17421e39941c6338a8c671019cfdd643c2f93f15c4fc6f33c194f0f329e1a329e87c9781e20e3b9988ce736329eabc8784e20e32922e3e944c6f31419cf4c329e6a329ec9643c95643c7dc9786e24e3e949c6339b8ce731329eee643c23c8781e24e35940c6730719cf35643c5792f13c43c6d39a8ca70d194f47329e59643c05043c89e0c877ff13f0ff79609377d45f07db329d5f04b626161f4d757e29d80a755ed6715c989e6b7be4ba512757efe5a3af24cc8bbf22e05846c2338b8ca723194f1b329ed6643ccf90f15c49c6730d19cf1d643c0bc8781e24e31941c6d39d8ce731329ed9643c3dc9786e24e3e94bc65349c633998ca79a8c672619cf53643c9dc8788ac8784e20e3b98a8ce736329e8bc9781e20e379988ca7868ca70719cff3643cd793f1bc4cc6733719cf20329e51643c33c878ba90f13c41c69324e3b9858ca71d19cffd643c43c978c691f13425e32923e379968ce732329e6bc978ee24e31948c633898ce711329ee9643cddc8782692f1cc21e3e945c6731319cfbd643c43c878c690f13c4dc6534ec6d38a8ca7988ce76a329edbc9780690f15c4ac6339c8c671a19cf04329e0a329ef6643c3790f1bc42c6534ac6730f19cf60329ed1643c9dc978ae20e379928ca70f19cfad643cf3c878fa93f10c23e3994ac6339e8ca70319cf73643cd791f1bc40c6731719cf43643c23c978ba92f13c4ec633978ca73719cfcd643cfdc878aac878a690f18ccd118f7ab75dde9d0c800ba724e49702cf3c073c8eea598adf35f82ec6f52aad961b5a2d30b42a86324b40bfe50ef42b00bfb26e995f0e3c727e24acf84d85174818c536cf314f02ea2c53a67d6039f0b8d8271dd53315ab2b8c3abd60d15dca60acae70504fdbbe23f32b80e7659d17d604947b9984516c4b1df324a0ce32658ad515c0e362df7154cf54acae34eaf4b245772983b1bad2413d6dfb8eccaf049e57745e581350ee151246b12d77cb539e803acb94295657028f8b7dc7513d53b1bacaa8d32b16dda50cc6ea2a07f5b4ed3b32bf0ab68367f6cc3666c523fd6d853501e52691308a6d85539ef2d204d459a64cedd82ae071d1ce3bd23dd58ead36ea34c9a2bb94c1585deda09eb67d47e6575b7c9704f16ab1a60e5aacb1f0acc9b116e22f5be6250d90d9ebec758e62f63a7b9da398bdce5ee72866afb3d7398ad9ebec758e62f63a7b9da398bdce5ee72866afb3d7398ad9ebec758e62f63a7b9da398bdce5ee72866afb3d7398ad9ebec758e62f63a7b9da398bdce5ee72866afb3d7398ad9ebec758e62f63a7b9da398bdce5ee72866afb3d7398a994167c523e3df096b02ca4d266114db4ab73ca9f7822607874f05c67c12f26b806795037d1cd533d5877cad51a7c916dda50cee5f6b1dd4d3b6efc8fc5ad80ed930af6e80cc5ee7fa312b9e293a2fac0928378584516cabdcf2a4dab129c1e153a6766c2df0b868e71dd533d58ead33ea34c5a2bb94c1fd6b9d837adaf61d995f07dbc1337b661bb3e299aaf3c29a8072534918c5b6c6294f59eafdc6a9c1e153a6766c1df0b868e71de99e6ac7d61b759a6ad15dca60acae77504fdbbe23f3eb613b64c3bcba01327b9dbdce51cc5e67af7314b3d7d9eb1cc5ec75f63a47317b9dbdce51cc5e67af7314b3d7d9eb1cc5ec75f63a47317b9dbdce51cc5ee7fcd159f14cd379614d40b969248c625beb94a763eab9c3b4e0f029d37387f5c0e3e2b98c23dd53cf1d3618759a66d15dcae0feb5c1413d6dfb8ecc6f80edd0d899573740661f1bb961f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e6286686d8503cd3755e5813506e3a09a3d8d6b9e5497df7607a70f894a9dfce06e059ef401f47f54cf5dbd968d469ba45772983fbd74607f5b4ed3b32bf11b68367f6cc3666c53343e7853501e56690308a6dbd5b9e543b3623387ccad48e6d041e17edbca37aa6dab14d469d665874973218ab9b1cd4d3b6efc8fc26d80e9ed933db9815cf4c9d17d604949b49c228b60d6e7952edd8cce0f029533bb609785cb4f38eea996ac7361b759a69d15dca60ac6e76504fdbbe23f39b613b7866cf6c63563cb3745e5813506e1609a3d8363ae649409d65cad48e6d061e17edbca37aa6dab12d469d665974973218ab5b1cd4d3b6efc8fc16e099adf3c29a8072b34918c5b6c9314f02ea2c53a658dd023c2ef61d47f54cc5ea07469d665b74973218ab1f38a8a76ddf91f90f80678ece0b6b02cacd216114db66c73c09a8b34c9962f503e071b1ef38aa672a56b71a759a63d15dca60ac6e75504fdbbe23f35b8167aece0b6b02cacd2561141bb663731df114193c45162d8e956fa5450f9d3f41ff26e0ff3d80d155db32d76094798c71b115e540b356064f2b43b363e95b6951017935e1f6aa004686edd52a079ab53178da189a1d4bdf4a8b9e3adf5affe2f6ea098c0cdbab0df038689fcb13068f9a321dbbb73ad6c7513d53c7ee6d815d773c0e49193c766f73504fdbb984cc6f83ede0993db38d59f1f4d579614d40b9be248c62c373feedf1f394270c1e35656ac7b63bd6c7513d53edd88ec0aefb76d05dca60acee7050cf02f02beb96f91db01db2615edd0099bdcef563563cfd745e581350ae1f09a3d8b601cfcef879ca13068f9a32b5633b1debe3a89ea9766c5760d77d27e82e6570ffdae5a09e05e057d62df3bb603b64c3bcba01327b9debc7ac78faebbcb026a05c7f1246b1ed009eddb1f3a4c77c411e35656ac7763bd6c74d3dd3edd89ec0aefb6ed05dcae0feb5c7413d0bc0afac5be6f7c076f0cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfcccdac7806e8bcb026a0dc001246b1ed029e0f63e7493f77401e35657aeef0a1637ddcd433fddc616f60d7fd43d05dca60acee7550cf02f02beb96f9bdb01d3cb367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed93373332b9e813a2fac0928379084516c7b80e7a3f879ca13068f9a323d77f8c8b13e8eea997aeeb02fb0ebfe11e82e653056f739a86701f89575cbfc3ed80efb3cb367b6302b9e413a2fac0928378884516c7b81677fec3ce9e7a7c8a3a64cedd87ec7fab8a967ba1d3b10d875df0fba4b198cd5030eea59007e65dd327f00b64336ccab1b20b3d7d9eb1cc5ec75f63a47317b9dbdce51cc5e67af7314b3d7d9eb1cc5ec75f63a47317b9dbdce51cc5e67af7314b3d7d9eb1cc5ec75ce1f9d154fa5ce0b6b02ca5592308a6d1ff07c1c3b4fc7d284c1a3a602633e09f98f1debe3a69ee9e70e0703bbee1f83ee5206f7af830eea59007e65dd327f10b64363675edd00997d6ce486d9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a3987d6c78e628661f1b9e398ad9c786678e62f6b1e199a398196243f154e9bcb026a05c1509a3d80e00cf27f1f394270c1e351518f349c87fe2581f47f54cf5db3914d875ff04749732b87f1d7250cf02f02beb96f943b01d3cb367b6312b9e613a2fac0928378c84516c0781e753073c0983474d99dab14f1debe3a89ea976ecb3c0aefba7a0bb94c158fdcc413d0bc0afac5be63f039ee13a2fac0928379c84516c8780c745ac2a9e228347e63f25f0adb4a8d6f913f42f6eaf6a6064d85e4539d0ac95c1d3cad0ec58fa565a8c81bc9a707b8d014686edd52a079ab53178da189a1d4bdf4a8bb13adf5affe2f61a0b8c0cdbab4d0e343b96ede1b1dcb78f659c7acd8f9de605c750f38263a87981d79c4a7307c797323c9605c0805312f29f01cfe7f1f3a4ee717d9605cfe7c0f3c3f8793a38aa67a95aef8f803daef52aad7e6c68f599a155319441861f3bd0af00fccaba655efc7966cf1cc58ce7b6c29a80729f92308a4dda2057ed86aafb157a5db2fe66613a7472ad5f17cf1ef0be6b33bd5ee1107f8550e6fe92dab29f6bb622f8bf6c37559f8386cdd1fbc01d6ccfc0645efc1505b9bb0f9ae9be2c6ae1e2d94db6c7fd83169eefe2e329c5fd1c7d1d7054f76c9ea31db0f0c458f70e51cf10f7c75ff754fbd15eaf4bd6aff6d12f4e76aa7939ee7bd27eb437ea5c08653a95d496fd06da0f5b5be17adf94737273df6c12d4b667c255a2ede6f395efb45dca7d02e5b1cde9a17f71ffec017575d52e46dd63c276d16cbb5d6a6f3ee3337d17832e9f906a667b4e813a5658b82b08b8311e73b99fc9ba6dcfc82a0c1dd934c36dfd8945c79e16ee9e04dc8cfb754f434736cd8eb65ff7b570f725e066dcaffb1a3ab26976b4fdba9f85bb1f0137e37eddcfd0914db3a3edd7fd2ddcfd09b819f7ebfe868e6c9a1d6dbf1e60e11e40c0cdb85f0f307464d3ec68fbf5400bf740026ec6fd7aa0a1239b6647dbaf0759b807117033eed78382c37564d3ec68fb75a585bb92809b71bfae347464d3ec68fb759585bb8a809b71bfae327464d3ec68fbf5300bf730026ec6fd7a98a1239b6647dbaf875bb887137033eed775edb7cfba5f575bb8ab09b819f7eb6a434736cd8eb65f8fb1708f21e066dcafc7183ab26976b4fd7aac857b2c0137e37e3dd6d0914d33db7eede8bdbcb26cdf133ce4549ff478cd87b2e0f918785cc494a3382875d4cf25d53775bfa1d521432b1c07e300e8e7a02f4cc6f7fbc59f67f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b667e66fcc6213e5f91729f90308a0d9f49b9b8cfafea7ea55e97acbf5998ba9d5aebf740ec7ecb4a0b0c7f49e0107f8550a6c979b5657b6ab6a2e0c8ed86e35ae3b6dc177b1dd2dbd28c7f99177f45509ffdc0e3e0fdfc14cf0183e780450b7cef341edf6543dc685c56aabebfd332a8ddcefb8cfaa0a61fc5eeff704d0b0c4d3f72ec3b111cbe3d8501a724e491c7c5b36147f54cb5057b8d3a991a1743998ba19e7b1dd4b300fccaba657e2ff0c884cfe45dc56060f004167d646a42c65345c6d38f8ce742329e9bc9784e27e3e94dc6f338194f57329e96643c23c9781e22e3b98b8ce73c329eebc87872710e9e0dcf73643c1dc8784e22e3194fc65348c6338c8ce720194f7f329e1f90f1dc4ac6733919cf99643c7dc8789e24e3e94cc67305194f828c673419cf60329e7bc8784ac978da92f1dc40c6730a194f05194f7b329e09643ccdc9788693f15c4ac633808ce712329edbc978ce26e3b99a8ce769329e72329e62329e56643c63c8788690f1dc4bc6730119cf4d643ca791f1f422e39948c6d38d8ca70519cf23643c03c978ee24e339978ce75a329ecbc8789e25e32923e339918c671c194f53329ea1643cf793f1b423e3b9888ce716329e33c87892643c4f90f17421e3399e8c671419cf20329ebbc9784ac878ae27e3799e8ca70719cfc9643c35643ccdc8781e26e379808ce762329edbc878ce22e3b98a8ce729329e4e643c2790f11491f15493f15492f1f425e3399f8ce746329e53c9787a92f13c46c6d39d8ce738329e11643c0f92f1dc41c6730e19cf35643c5792f1b426e36943c6f30c194f47329e02029e4470e4b79812f0ff0360936f067d0cb62696f5c9735829af8e8b53db1eb9ee2696757f6461409d3e84ba2475bef4fb4d299dd05712e6c55f11707c44c2d3918ce719329e36643cadc978ae24e3b9868ce71c329e3bc8781e24e31941c6731c194f77329ec7c8787a92f19c4ac6732319cff9643c7dc9782ac978aac9788ac8784e20e3e944c6f31419cf55643c6791f1dc46c6733119cf03643c0f93f13423e3a921e339998ca70719cff3643cd793f19490f1dc4dc633888c671419cff1643c5dc8789e20e34992f19c41c6730b19cf45643cedc878ee27e3194ac6d3948c671c19cf89643c65643ccf92f15c46c6732d19cfb9643c7792f10c24e379848ca705194f37329e89643cbdc8784e23e3b9898ce702329e7bc9788690f18c21e36945c6534cc6534ec6f33419cfd5643c6793f1dc4ec6730919cf00329e4bc9788693f13427e39940c6d39e8ca7828ce714329e1bc878da92f19492f1dc43c633988c6734194f828ce70a329ece643c4f92f1f421e339938ce772329e5bc9787e40c6d39f8ce72019cf30329e42329ef1643c2791f17420e3798e8c673f19cf75643ce791f1dc45c6f31019cf48329e96643c5dc9781e27e3e94dc6733a19cfcd643c1792f1f423e3a922e36942c633d6e0c1ffab77c3e4fc48be1d5408ff1fa83b97b7d6eb9232f28e92bad7b5c7b0a9faee7654df3d41ed9484f9dd505f61df033c7b1cf17c68f098be8b205f019aed326c8a71a723c65d06a3ccef0446d16f17f0ec72c4b3dbe0317d1741be2768b6c3b029c6ed8e1877188c32bf1d1845bf1dc0b3c311cf4e83c7f45d04f9bea0d936c3a618b73a62dc6630cafc566014fdb601cf36473cdb0d1ed37711e4fb81661f1836c5b8c511e30706a3cc6f0146d1ef03e0f9c011cf5683c7f45d04f9fea0d966c3a618373962dc6c30cafc266014fd3603cf66473c5b0c1ed37711e40780661b0d9b62dce08871a3c128f31b8051f4db083c1b1df16c32784cdf45901f089aad376c8a719d23c6f506a3ccaf0346d16f3df0ac77c4b3c1e0317d17417e1068b6d6b029c6358e18d71a8c32bf061845bfb5c0b3d611cf3a83c7f45d04f94ad06cb561538cab1c31ae3618657e15308a7eab8167b5239e35068fe9bb08f255a0d94ac3a6185738625c6930cafc0a6014fd5602cf4a473cab0c1ed37711e4878166cb0d9b625ce68871b9c128f3cb8051f45b0e3ccb1df1ac30784cdf45901f0e9a2d356c8a718923c6a506a3cc2f0146d16f29f02c75c4b3cce0317d1741be1a345b6cd814e322478c8b0d46995f048ca2df62e059ec886789c163fa2e82fc18d06ca161538c0b1c312e3418657e01308a7e0b8167a1239e45068fe9bb08f26341b3f9864d31ce73c438df6094f979c028facd079ef98e7816183ca6ef22c8df0d36e1ed0eb6f775be1bd8ded3f9ae607b57e7bb80ed1d9def0cb6b775be13d8ded2f972b0bda9f31dc1f686ce9781ed759def00b6d774be17d85ed5f9de609babf349b0cdd1f93e609badf357816d96ce5f0db6993a7f0dd866e8fcb5609baef3d7816d9ace5f0fb6a93a7f03d8a6e8fc8d609bacf337816d92cedf0cb65774fe16b0bdacf3b782ed259dbf0d6c2feafced607b41e7ef00dba33a7f27d8eed3f9bbc07648e7ef01dba73a7f2fd83ed3f9fbc1f6439d7f006c9febfc8360fb91ce3f04b61febfc60b0fd44e78780eda73a3f146c3fd3f987c1f6739d1f01b65fe8fc2360fba5ce8f04dbaf747e14d87eadf3a3c1f61b9d1f07b6dfeafc78b0fd4ee76bc0f67b9d9f00b63fe8fc6360fba3ce4f04db9f74fe71b0fd59e79f00db5f74fe49b07da1f34f81ed4b9d7f1a6c5fe9fc3360fb5ae79f05db373aff1cd8feaaf3cf83ed6f3a8f63ddfe5de74b8278dbd96f83daa9047c8b3f55e61f3adfdc2823cb16429913750747f58c43bd8b27edb0b4cbca26edf0fb609376f83db0493bfc2ed8a41d7e076cd20ebf0d366987df029bb4c36f824ddae137c026edf0eb609376f835b0493bfc2ad8923a3f176cd20ecf019bb4c3b3c126edf02cb0493b3c136cd20ecf009bb4c3d3c126edf034b0493b3c156cd20e4f019bb4c393c126edf024b0493bfc0ad8a41d7e196cd20ebf043669875f049bb4c32f804ddae147c126edf07d6093fde55bb049db7c086cd2367f0a36699b3f039bb4cd3f049bb4cd9f834ddae61f814ddae61f834ddae69f804ddae69f824ddae69f814ddae69f834ddae65f804ddae65f824ddae65f816db4ceff1a6cd236ff066cd236ff166cd236ff0e6cd236ff1e6cd236ff016cd236ff116cd236ff096cd236ff196cd236ff056cd2367f0136699bbf049bb4cd5f814ddae6afc1266df337607b5ee7a5ad6e01367956aca6d2ef39e1383c78ce2f4cc920deb61fa724e49f83bacbd4848ce755329e2a329e7e643c1792f19c4ec6731f194f4b329ee9643c6f91f1fc958c673519cf2a329e6d643c5bc978ce23e3d94fc67312194f2119cf1c329e61643cef93f11c24e3e94fc6f303329ecbc978ce24e33944c67305194f828ce70d329ea9643c2bc9785690f17c40c6b3858ca7948ca72d19cf29643c15643cedc9789a93f1cc22e3194ec6f32e19cfa5643c03c8782e21e3399b8ca7988ca71519cf64329e31643caf91f12c27e35946c6b3998c671319cf05643c7bc9783e24e3398d8ca70519cf0c329eb7c9780692f19c4bc6339f8ce732329e13c9789a92f1cc25e3594ac6b3848c671e19cf46329e0d643cedc8782e22e3d943c6b39b8ce70c329ee3c978a691f1bc49c633888ca7848ca70719cfc9643ccdc8786693f12c26e35944c6f31e19cf7a329e75643cdf92f1ec22e3d949c6731619cf09643c45643c53c878aac9785e27e3a924e3e94bc6733e19cfa9643c3dc9788e23e39949c6b3908c670119cf3b643c6bc978d690f1ec20e3d94ec6730e19cf95643cadc978da90f14c22e32920e04900470036f97f53b07da3f307c126dfebd90fb6af757e3ed8bed2f9e7c1f68cc5d6c4c2270cdf804dde6d7e166c72fff36bb0c93b015f814dceabc4bf9a9fdbf648fe26b08cf8696ae1477f5f59b8248fdb5b964906f16e6ff4950c8efc9e5211703c4bc233898ca70d194f6b329e2bc978ce21e3d94ec6b3838c670d19cf5a329e77c8781690f12c24e39949c6731c194f4f329e53c978ce27e3e94bc65349c6f33a194f3519cf14329e22329e13c878ce22e3d949c6b38b8ce75b329e75643cebc978de23e35944c6b3988c6736194f33329e93c9787a90f19490f10c22e379938c671a19cff1643c6790f1ec26e3d943c67311194f3b329e0d643c1bc978e691f12c21e3594ac633978ca72919cf89643c9791f1cc27e339978c672019cfdb643c33c8785a90f19c46c6f32119cf5e329e0bc8783691f16c26e35946c6b39c8ce735329e31643c93c9785a91f11493f19c4dc6730919cf00329e4bc978de25e3194ec6338b8ca739194f7b329e0a329e53c878da92f19492f16c21e3f9808c670519cf4a329ea9643c6f90f124c878ae20e33944c6732619cfe5643c3f20e3e94fc673908ce77d329e61643c73c8780ac9784e22e3d94fc6731e19cf56329e6d643cabc8785693f1fc958ce72d329ee9643c2dc978ee23e3399d8ce742329e7e643c55643caf92f13421e3196be199ef8847faaec8ba657e7e23f7bddbf0bd3b4f7cef347cefcc13dfdb0ddfdbf3c4f756c3f7d63cf1bdc5f0bd254f7c6f327c6fca13df1b0cdf1bf2c4f73ac3f7ba3cf1bdc6f0bd264f7caf327cafca13df2b0cdf2bf2c4f732c3f7b23cf1bdc4f0bd244f7c2f327c2fca13df0b0cdf0bf2c437f3f5b7eaa72cdf82d8ab7f13f07f1c3f6ebf23c6f906a3ccef0746b1e1f8ef3d1cf1445dbbf720f0adb490be8bf20e7d02fe5f018cae62aa87c128f3b698c2f1462b1cf144dd73a820f0adb4906f39ca37b112f07f1c3fc5554c55188c326f8b291cffaba7239ea87b253d097c2b2de45b8ef20de004fc1fc7277215533d0d4699b7c5148e37d1d7114fd43d9ebe04be9516f22c469e9927e0fffd80d1554cf5351865de16535b81a79f239ea87b53fd087c2b2da4ef99f4594ec0fffb03a3ab98ea6730cabc2da6b6004f7f473c51f7d4fa13f8565ac8bb51f20e6b02fe3f00185dc5547f8351e66d31b509780638e289ba173880c0b7d262a0cecb377212f0ff81c0e82aa606188c326f8ba90dc033d0114fd43dcc8104be951683745ebec19980ff0f0246573135d06094795b4cad039e418e78a2eebd0e22f0adb4a8d47919032101ffaf0446573135c86094795b4ce1786d958e78a2ee195712f8565a48df18e9339880ff5701e320478c9506a3cc0f0246b1ad029e2a473c51f7baab087c2b2da4affb4afd9b80ff0f03465731556530cabc2da65600cf30473c51f7e88711f8565a0cd779f9864702fe3f1c185dc5d4308351e66d31b50c78863be2897ab6309cc0b7d242c63e906ff225e0ff385eb6ab981a6e30cabc2da696004fb5239e4506cf228b16c7cab7d242befdb258ff26e0ff6380d1554c551b8c326f8ba945c033c6114fd4b39c3104be9516d2b773a1fe4dc0ffc702a3ab981a6330cabc2da670fce5b18e78a29e418dcd81efa8e729b9f01df56c2017bea3ee73e7c277d43ddb5cf88ebaff980bdf51f7d272e13beabe502e7c47dde3c885efa8ebf55cf88ebaf6cc85efa8eba85cf88eba26c885efa8f3db5cf88e3a57cb85efa8f30edf9efbf63c6edfc7f2dc215fdbf363790c3d96c7127f6de0af0d72e5db1f4bfcb541ae7ce7ebb5816fcf73df9ecbf55741107d3db6cc91ef25866f99c7e72c4b1cf95e64f896797c66b0c891ef05866f99c7fbdf0b1cf92e327ccbfc821cf86e65f86e9543df6d0cdf6d2cbe5d6cef4470f8f5b730e094843cc6c042073c8eea59aad6bb58afebbb18d76bbb6f63ee2fc5506631e8e7baed9075676a3b5ac4e7bb34013e64cc38659367b1ef834ddad0f7c026cfd8df059bb4f3ef804d9eefbc0d3679fef316d886ebfc21b0c97358ecff2ecfd2b783ad52e7b1dff5209ddf0a36e99784fd7da56fd916b049ff40ec672a7d3c37814dfae962ff46e96bbd016cd25f1efbd5c93b0febc026efad607faefd3abf066cf28d4aec47f4b5ceaf02db373abf126ccfe9fc0ab03dadf3f781ed4b9dff166c4fe9fc02b07da1f30bc1f6a4ce2f06db5f74fe4db0fd59e7df00db133aff3ad81ed7797c2fec4f3aff21d826ea3cbe8ff4479ddf0db6c7741edf83f983ceef04dbef75fe35b04dd0f957c156a3f373c1f63b9d9f03b6f13a3f1b6cbfd5f959601ba7f333c1f61b9d9f01b65febfc74b08dd6f96960fb95ce4f05db289d9f02b6913a3f196cbfd4f949607b44e7ff0ab65fe8fc22b035d1f9256093f105b1cf877c337319d864dc6ceccb2363098c05db713a3f066c2d74be1a6cf29db5e16093b17a87812da1f355603b41e72bc126e73a83c02663ad0c049b9c970c005b6b9def0f363987e8073619fbb02fd8e47b9e3dc126637a57804dbee3df036ca7eafc7cb0c9f866fbc126df5c3b08361947f86bb0c9b796bf01db593aff1cd864cc97a7c1768ece7f09b67375fe29b0c9f739bf005b89ce3f09b6b63aff17b09daff37f069b8c0ff604d8e49b6e8f834dc6e1fd13d8e4dbc913c176b1ceff116c97e8fc6360937154fe0036194bf2f7606ba7f313c026df90ae01db153aff3bb0c9d816e3c176a5ceff166c3286c138b075d0f9df80ad4ce77f0db68e3a3f1a6ce53aff2bb075d2f95160ebacf323c1d645e77f09b6ae3aff08d8bae9bcb4336a7f56fbf9013d9f0ce23dcffe38387cca749e2d0cc813e7796b31f0a0af7db1d7bd2c758e2c6d4113bd5e89a17de07b6fecbed3e7e71fe97515eaf5ee357c174299d6ba7150cbc931bfa95e6ebfb11cde139275cb325782fd4363ddad757d3f7254dfbd069370a30e52e664cda48e8ddb75ded1fbf0656a1f90580b40439c92901706375a9595e2b9705d783e029e7db1f3a4af7d5dc404ee5b715ffb9af744cd582b86327b41bf0f1de887fbbaac5be6c59f67f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b667e66c57340e7f159b3943b40c228b67dc0e3e23e3f3e8795f5abe73acbceabf5bb2f76bf873fdf6ba6d75b6ad4b910cafc1d9e39add2791cc753b65bd4b674f09c30e3b6147f45501f7c1674c011cf3e83679f450bc997c4e6bb6c881b8dcb4a55bf15f58c7dbfa1eb018ba6aef6d77d7a5d0586a6b8bf7e6cf0e0b3d122e0fd44ff26603d9f401d1cece319e342fce1beb40f6c92ff18185d6c673c96487b20cfc3f1d9b494d96f3c178f7fdb9795ba6c37f6409d92c191f15d08650e41dbf799ce63df9003a0dbef2cff9729d3736ad14fd57957fc754e6ddf9dc099043fe87b07b0c6e4fbb0f7510a74123f622f84fc6fa13f8794133d446b6157fb88f4e9447673b98f8de58aa1cc6e4bfd9341bcf5df65f0ec3298d536f911c4d9efe0f8efaa4dda1da1d195a09194d9071aed77c4b3cfe0110ef1a7cac8f66f6e9491650ba1cc97d046a9ba483b2ff5c4be2d780c70751c135fb26e99b79d1b7f048c661d557c743bb596775fecbc0dabffd7bf417b1177ffaf7f83182a008ec0587f29ac5fb89a07d1c71629f33f8de3a88b6b0cdc96a69ea8b394f95fd00eb52f49e7b339d73f56d76d51e7fabb1df02482c3afbdd594e9f88ec7983d0e781cd5b3d476ecfad0a8533194b918eae9e03c26e37bb5bbc0b78b6d8e5ac839d45e438b4228d3b224fd2b6d47948e78adfa514eea52663d1f2cb5d445cab42aa9ad4b0bb0c7c9e472bbed803aa9f5eeb3d455ca9c5c52abcba93a9f80ed84f74d2eb2fc5fa64ced018e6bb32dfe3aa7b6ef56e04c821ff4fd01b0c6e4fbb0ef6ac8f9bef8117b21e42f2ca92d2be5440fd15ad8d53e22effc21bbb9dc5e63b96228b3dd52ff64106ffdb7193cdb0c66b54dce28a9cd4b1cb96c37b74768540a1a4919bc7f2cc7767cefce76dcdfe7883beab8bf0f18cd7613cf5d5cb2ed37d8cc7ba8b6f3412983e76452a66349fa57b5b3094b59f3deb08bfb98f86e6900f5088cbaca8431e0e0dab01caf9da49d123fedc1be4fe745e7f686768550a66749fad7e179b7f5dea5797d87d714c26dee5bf8eec75525b5dc2aee653bedd3bf4560fb54ffb6089c5ca795dbee190a87ed9ee10d25b5ecb8ac701db2d4c5bc466e121c794ffd3ba32cde77cbb41cfa52d3416319a5efa716a67d869f16b06c6cef6a74282d8de23f087c1f1bcc365d653fc098dba7f3e6be82d7c652e6be92f4afb4496659b5edbf38b9561fd98ea21db62718931f036352e74bbfdf94dae70e1af59779f1a718e57ecf41e089bfed4abfaf94cd7de0fdc0e3a26d77d44697e231b6456cebed56693bfe7f6c6895c3e7b5d663bef9ccbd85918fc7775995edfe934d8bbd161e57cf51a2b4d86bf11d9f169d87d8da399b16b9ecfb10a5c58716df316a310cef7b66d2628f85c7c5bda84c5aecb1f88e4f8b2ea5999e6ba016bb2d3caeee3d446921feb265fe9080b985918fc77779a5ed3e994d8b5d161e57d7cd515aecb2f88e4f8b0e9df11e5d262d765a78e2bf3f97598b9d16dff169d1b51bdec3cba4c50e0b8fab67ba515aecb0f88e312e86daeee5d8b4d86ee1d99e632db65b7cc7787ed8d976afcda6c5360b8f83fbae19b5d866f11da31683f1be6b262db65a78b6e6588bad16dff16951d9c9764fd8a6c507161e57f784a3b4f8c0e23b3e2d067755beb7d4418b2d169e2d39d6628bc5778cd750a9b8d85c072d365b7836e7588bcd16dff16951953ad7da54072d36597836e5588b4d16dff169519a3aa66eac83161b2d3c1b73acc5468bef18e322753db9a10e5a6cb0f06cc8b1161b2cbe633c8ea4e2627d1db4586fe1599f632dd65b7cc7a7c5b0d4fda77575d0629d85675d8eb55867f11de33d97545cacad83166b2d3c6b73acc55a8beff8b4e8983aa6aea983166b2c3c6b72acc51a8beff8b4189a7a26b6ba0e5aacb6f0acceb116ab2dbe633cef4cb517abeaa0c52a0bcfaa1c6bb1cae23bc6f3ced4fd8b9575d062a58567658eb55869f11d63db993aef5c51072d56587856e4588b1516df319e77a6b4585e072d965b7896e7588be516df319e77a68e23cbeaa0c5320b8fabf144a2b45866f11d635ca4dacea575d062a98567698eb5586af11de37dad54dbb9a40e5a2cb1f0b81aaf214a8b2516df315e8fa4eef12dae83168b2d3c8b73acc5628bef189f15a5cec117d5418b45169e4539d66211f8de1fbbef747f6ef1217db1ae30b4288432cddba67fa52f56948eb20eec57867559187b5dd2fdca1644d46521d445ca9c007569113819efa7dc515d5331331feaa4d6fb89a5ae52a64ddb5a5d4ed2f9046c9343a0dbf996ffcb5460cc27212ffaa93abf1f7f9d53b1fa1e7026c10ffa7e175863f2dd017d17e8247ec45e08f9b66d6bcb4a39d143b41676b58fccd3796437975b642c570c65e659ea9f0ce2adfffb06cffb0673eabd0788338923376d579a695e84465780465206fbec7de288c7ec43281ce24f9591eddfdc2823cb164299cba08dc27ea552cf447064bf49476d5907649775cbbcf82b06db7e6034eba8e2e310f4fd94b122641c09659371213ac27aba183655d7ae8eea2abe64dd32df1518659c8a2eb9672cab2b63678351f17477a0198ebd2153a6e34577e0e9e680c7513d53c7a11e469dba1a752a8632f86e630f07f52c00bfb26e99ef01be5d6c73d4428ec9971a5a1442991b8cf3c7281d651d2a7ebb58ead2cb715d64ddd22ef5ca81ef0ac37727c37722387c3b0741e6fdab02987b3a6056ebed1dff7a4bf1bc4d624afc74823af5010de2aa13ae4bcef3fa18da1642fe4138cf937252568e5fc2ae6259b625b29bcb7537962b8632bd2cf54f06f1d6bfb7c1d3db6056dbe42e38b773b03fa462a097c121f39d40bbde11daf502eda40c1effba38d2aea7c1d3d3f0ad78e41ca707d8e45c41f813f0ff8e39e036dbbd1e166eb1e13871b6739dcef133663cd7e90c8c62eb093c158e3433b7f5a5863e785c6e6e9491650ba1cc383836262c65d57e7771416dbd9a6a7b6cef8ee936bdb903bd709cc600f4090c0d03d04bead9cc01cff141ed588de36baac70d7e78e85d43d38f1e05add0c0c4df024b359a800df34d2db620387c48ca42b0c99094cdc0d6c4900587c294f232a49d0bb9500f5977a1c1d90258e2f48dc379ca9429748e031e17a1ac424786f4d4a173dfb8113543313e9a199cf5891df5bfa619ca45adcbd57630f78924cc9b3158e8c87f53a86f12e6c59fda3632b4ea98c1431eed33eee109a3868eae198f42993b36e60b82c33780f91b25b8ab9d0e03002b8c8d4333a35ed860c8ff64c31c1f3f67398e996b6a13803f998e07dd5a3ad04dad5fc6be1d3278e4c83b26548e1c31e4ba09a387d48ca81e8d5bb385a15cd49696ff37079bad89c7b26ac2660b973dce62b34d38ca700bb0c991ab25d884e778b03585bc9437b78c9370bd10d62fbb94fa9f12a799aef871416d08c8e158b5ab6aff55a772ea13b2ea54480d6dac36a71aba58dd31544313abafd8a9a187d550c36a68e13383f4d0c16aa8e07382f450c0ea6b1725417a68dff383daa17bd57408382f0ad243f3aadb349704e9d32e35b46ebb203d74aeba75d93e487fe64dbd5baf4edfd56d0175caab2ef1d4e5883a0555a79eeaf685ba95a54ee9d4e9b23a1554a76fea72a4b7d6ba4f98ae0ad3d561ba264cd786e9ba305d1fa61bc27463986e0ad3cd61ba254cb786e9b630dd1ea63bc2746798ee0ad3dd61ba27480fef7c6f901e7e5d0dff7c7f901e1afa81203d6cf483417a48e98782f470d38383f450d44382f430d54383f410d60f07e9e1ad4704e9a1731f0dd243edaa21784707e9e1b0d530d96af86c35f4af1a26580d29ac861a564315ab618dd510c86a68e42783f430cc6ac8e667c2f46c901ed2f9f930bd10a617c3f452985e0ed32b417a7870356cf894203dccb81a7e7c7a901eae7c66901ede5c0d7bae864357c3a4abe1d3d5b0ea6a987735fcbb1a16fead30bd1da67782f42309f528463da250b7ffd56330758b7a6190be75be38483fe2568ffc551708d5254475915919a4bb50a92e65aa8b9dea72a8ba60aa2ea9aa8baeeab2acba70ab2eedaa8bbf7ae541bd02a25e8951af08a957a6d42b64ea953af58aa17a4d54bd76a95e2356af55ef0bd2b7c50f04e947a5ea76b87a34a0e251ddbeff2c4c3f0cd3e761fa51987e1ca69f84e9a761fa59987e1ea4873056c31dabe192d5d0ca6ac865353cb31aca590d05ad8688fe63901e7a5a0d5dfd97203dfcf59761fa2a480fc1fd4d901e7efd6f61fa364c7f0fd33fc2f4cf30fd2ba81d4a1b1b8b33750ba3af5282c1353543478da929a9a92e19356164cd8831239f289938a2667849f56343c70d1b593d1117fe5c2f2ce380f719376ef013252346570d7dbca47a424d49f5b092caea09a3ab0e3b50ff532f74f6911e075755453bfbf7ef43fa7feae9b4a56efb6484f51b33d7ed84a6f510e4c4fa2cd4b969fd2af4a43e4ac9e5ecdde973dd92f123ab6b4a4a4b46877fc3836bf5c4a155ed4bf07fe34391c7d7948caf193caea664d8b8ea51251ddae37a271d5f8f4a1424dcc0b439b37ee2b4d2df52aa5788ed3baf1e0a7c715efd48ffdbf721fd1ff574daa2a41e352caecf426525f523ac28899465fc84ca9a718387d4442fdce7fb2c7c7d7daa796f3dabd9ac6d3d9c25eab3d0a56deb47787d7d9c8dcdc259f0ff015dc8f1201c4f06009b2d6c6f00000027cf1f8b08000000000000ffed9d77701cc795c6679118565810cc99902c538ce06291c104e64c9992ac1c98409116495024942ccb922cc939e76c39cb39675bce392739e7ecf2d5dd3f57bebaab525df76c3fe34373668d5dcd03df60df543d6ccfdbde79bffee64dcf6cf7ec20131497bf1acbb872adb1c705672ff47ebf7bcd3fb6a52dc16de539393329e1ac4909676d4a38eb52c2599f12ce8694704e4809e7c494704e4a9033037c5cbc9353c69b4d19ef79413af2b631259cb9947036a584734a4a389b53c23935259cd352c2393d259c3352c23933259cb352c2393b259c7352c23937259cf352c2393f259c0b52c2b930259c8b52c2d99212cef353c27941829ccb8193c6c82f74af8f77af8bddeb45ee75897b5dea5e97b936d6b975bbcd15c6561a6bf5de5b65998dd9c1f282f75ebbb10e639dc6badc7b2deebd6e633dc67a8df5195b6d6c8db1b5c6d6195bef34d9606ca3b14dc6361bdb626cabb16dc6b61bdb616ca7b15dc6761bdb636cafb18b8d3dc1d83e639718bbd4d865c69e68ec728fe50a63571abbcad8d5c6ae3176adb1eb8c5d6fec0663fb8d1d3076d0d82163878d0d183b62ec4663478d1d33f6246337193b6eec84b193c6068d9d3276b3b1d3c6ce181b32768bb15b3dcd6e3376bbb13b8c3dd9e3bcd3d8538cdd65eca9c6ee36768fb17b8d3dcdd87dc6ee37f680b1a71b7b86b1671a7b96b1671b7b8eb1e71a7b9eb1e71b7b81b1171a7b91b1171b7b89b1971a7b99b1971b7b85b1571a7b95b1573b163a105e63ecb5c65e67ecf5c6de60ec41636f34f626636f36f616636f35f636636f37f690b177187ba7b177197bb7b1f7187bafb1f7197bbfb10f18fba0b10f19fbb0b18f18fba8b18f19fbb8b14f18fba4b14f19fbb4b1cf187bd8d8678d7dced8e78d7dc1d8178d7dc9d8978d7dc5d8578d7dcdd8d78d7dc3d3fc9bc6be65ecdbc6bee37cdf75afdf7375e998ffbe7bfd817bfda17bfd917bfdb157ff116ffd27defa4fddebcfdcebcfddeb2fdceb2fddebafdcebafddeb6fdceb6fddebefdcebefddeb1fdceb1fddeb9fdceb9fddeb5fdcab9d576b9f512c4f0c8697fe20a13ea963a0d78eab93d8febc9d9d13aa75efd16b8bf3d7b9757acd387fbd5baff7fc0d6ebdc1dbce44b73ed1f3e7dc7acef34f71eb533cff54b73ed5f34f77ebd33dfff96efd7cf067dd7bc586155facafd6b932e0a3fcac015fbdf3d582af813607be09ce570f3edabf0de09be47c13c037d9f926822feb7c93484b63e7395f7f9054aee40fd8ed3626bd5d3717914b9ef7a0dd6e1313ef94e4790fd9ed3633f0dafc98eab6d5087933cdf972e09bee7c4de0735dd0bf8e39eb9be97ccde09be57c53c137dbf9a6816f8ef34d07df5ce79b01be79ce37137cf39d6f16f81638df6cf02d74be39e05be47c73c1d7e27cf3c077bef3cd07df05ceb7007cd45f2e04df85ceb7087c746dd7023ebace3b1f7c74cd7781f3d97e626206e2393ff551613cea9fc1f778ea9bc1b798fa65f05d447d32f896406cf22d857e857ccb9c8ffa28fb5e9f2bf707491d1385f0185e9df476cd96ed76d726bfdd700e6c5d30ac753fc4590d5aad77e504ef0f69c3d8741d4371c85f07e51d5097ea911e749e2176dbefaf71e5f5253ed7e77d2e0775d644b4bf3f48b6fd6b3d9eb51e733db49f29678f68ce8e7a293b67af80ba7eeed135cf78ccd9ddc0917cceb6b76bce8e7a293b6707a0ae9f7b74dd3b1e73f66ae060c8d96e9e9c2de435678be36041109d7bf4dd673ce6ec51e0483e673b356747bf949db3f7425d3ff7e8fbef78ccd95b8123f99cedeed66b83512f65e7ec0ba0ae9f7b3416331e73f67ee060c8d983dacf8e7a293b675f0775fddca371c1f198b32f068ee473b6972967db356783e29c661044e71e8d518fc79c7d103892cfd9433a3e3bfaa5ec9cfd04d4f5738fe64bc663cebed795ed3cc377dd3cc37cf07dcff916006ff2b97db88d29b7db34b78bf77a0441748ed2dcdd78cced875dd9e6f10fe0de03f2fd90ee4b00df8f9cef02f0fdd8bb6783e918e8d26360f46d2af718f805d4f57399e691c7e331f03de060c8d96ecdd9d1b7a9dc9cfd1bd4f5738fee69188f39fb6be060c8d91ecdd9d1b7a9dc9cfd27d4f5736f892b8fc79cfd872bdbeb859fb9eb8565e0fbb9f32d07df2f9c6f05f87ee97c2bc1f72be76b05dfaf9d6f15f87ee37c79f0fdd6f9dac0f73be72b80eff7ced70ebe3f385f07f8fee87c9de0fb93f37581efcfced70dbebf385f0ff8feea7cbdce67efc9a37bafbee27c76df9246fd41b2fb96eeb1a46dd3fa8a3188dde4c56e1ac3d8cd5eece688d82b19626721062d196fbd1fca2b7979f239e0c158ab928fd56edbde1a8cbeedab8027cfd0f62cc4180d4f1e78da92e709cf9f85e4b71beee3564fd32cc46a8576b533b42b03b168dbb44ef172e0c3febb3d82b12379c6420662d1b669bd0318c987e7133aafd3f163cf878b33c3bc0cc752784d44f1e8f94fc4b10afc54e791e9c36ccb1c5b23bc8fe7d636cfc79497615e502cda36ad53bc46684fdbd8331646cb98f718b9fa880cc4a26dfbb1f1785f39f69a8d6abfe6c0770efaa442a57d5223b08dc5754adcbe96129be37c958118d4b791e605f0539d99ee0709b66fdb06fd2ec3f15728f7fa0dfb83e4f3b890c7e37a343cedc0c371ec331daf793cef3f1a249b6b9d9e566d9e5639a8d301fa7532e857ea3a84e229b3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb37c66bcff02e737a9de0a218ce42b000fc7387ff80c28b72ddabe9dd7f929cceb243f6f51c8e39c25dd63b8d46b731dd4f97b66b8ee2f613edd9f1bc439cd15bcda8dea3e8bc6e0ecb958ce39c4b879e0a8f9cb96c462170e71cdb7d97bd4ec73c85a3d5d574468ca709fca084d339ea6789fe2728fc7e6e99cda61368eb9bf72e722512b2a2739b787f718f0ee9762ff41b950138cec3ff03cd39578ec917398345fdee5c5ae833aff9319de37742f2afdaf3bff9e275ba7dbdb367d6629f8bbbd6d4f719f258e7a6ffbadf059aaf37fd0a7bebe26f897661cf77f60bf1c405b71e98732ce9b277f1e2ecee3b797c1d3093c1cfd0cd3f5461e8f81a4e7f1bb3dada2ae63a84e17e8d7cda05fd4b528ad533c6556666556666556666556666556666556666556666556666556666556666596cf8cbf1525d62cd42b08611ca37b1fc2f90c7afe0b6ddfceeb3cb766382ef73c1ccd392df3da5c0775be5d335cf785aedc189c7dbf43dcbe6498cf2bb92f295e23b407e782b87ecfdde1f174446841e596c46217e7f193d778781ebfddd3b510a129d7f18a73aca8291eaf6d1e0fce8d360667df5b9285ed8cc5bd43717941f1f058ea001f95f1f7d11cfb19cf25fe7d3d140fe7af1f72da4e09b8f67d21cfd96ff4409bfa83b3f3bb0eeabc07fabef7b932dec381f78e3c1cf13e2da5e6a9493f9667d9e58bf3beab81b31fe260ec35c09a50ec368c9d714671c85f07e5cfd40cd7a57aa407694decf618a1678221bbffb936ef7339a8d31bd1fefe20d9f6f7793c7d1eb3dd271f843c7b18ceff5c7d526f8c464b4123aa83d7415ce74efffe4affbe42ec471bbc3af4d93aa8f315e8a3e2ee1f8d3a07709fc768db51e731ffbc309afb3cabfd3ead47a0bf48fa3ead472087f03eadc0dbfe32d83e713504f1e716aaf3736ffbfe35397d06ef03a33abf82fea2c9ddb3d8189c7dfd8df74c8dc5f7abb8fba4291e5ed7d07ba369bb65ee81fafd0932634e200b5e27509dbf78fbac3b867b55c467ff1ef359d28a9e4385df5f7cfdac0ebdf099fe4474281ef37d5e5b28af7ba12d54e7bfbc6bc0e4af5b8ad780c9b775e43509f5031d116da53aff0dc7da3fe11a8ff6137eef68a83dfb7d5a4a5d03927eb6cd63fd7c608c2de1f9c0f5b5c375fde7fc92d6e53e1fb8cbfb9cc4e703ff2fe45903dc87ced557af89d16819684475f0b741741ec167f9468d759cabefd3782d85fd731368caf5bb03fffce78fab455d232cf334c66b84e98e798ad3d9afeb8f17d2f932c9fb86f1b712ad10177f2bd1caa4671e74eb8775bc2e3897b1f34cb1e39e399d1f83d871cf9c1e8bd8cd5eece6318cad9aabe6923467782672f8fb337c66a95d4a5d9712430e3e579302c6da1430d6a580b13e058c0d29609c9002c68929609c9402c6c92960cc02e3b93cb733e853a8541faefd55ea5a036333fcef9242b9ffbf83f97fa994bcf6c1d80cdfe9422d5606a3d702bfe7713cfba1dcfff5420cf8bf0ba6a680715a0a18a7a78071460a1867a68071560a1867a780714e0a18e7a680715e0a18e7a78071410a1817a68071510a185b52c0787e0a182f4801e3e352c078610a181f9f02c6c5ca9808e3725ec642a58c9687e37ffe3d96ff39c6c0938fbae794e9fed9b2ffdf1af3f349db2a7d6e1cde5bc2fb3fe11edbb3ed38ee1d29f7d976a5fedf2a1363a15246ae7b81f0bea3d1f044dd1fd4c6cb58a89491ebf72ff81bbdd1f07481669d119a3130162a65e4ba57aedc7b39f19efeae08cd18180b9532e27dd509f2849a7597c1d3039a754768c6c058a89491ebbee42cc4180d4f2f68d613a1190363a15246a6dfb6859af596c183bf01eb8dd08c81b15029a3e559cda4595f193cab41b3be08cd2431224fd2cfc9ee8b88c5f19bc172db4e0cc83829058c9353c088f74970f45fa5ee93e8e3d5a750a93e5cfbabd47d12189be1f731a116f87b887fa7c55a5e9e92f74960ec754c5ae0ef55fe9d16eb8087e3f7335988311a1e62c8c1e7a6a680715a0a18a7a78071460a1867a68071560a1867a780714e0a18e7a680715e0a18e7a78071410a1817a68071510a18f1bb2ac3b562c9ef2febc679ecb8ef2ae33d76dcf792f11e5bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae795e0db135cf35cfab21b6e6b9e67935c4d63cd73c97143b0d63fcca38fe1891a725399e3cb61d63f50b687b7f044f86a9ed186b8380b61343da18d7a78071750a1855c7e23d8895305a9e8d4c3c1bcae0d9083c9b98783696c1b309783627cf13e6d4a632788821079f5b9d02c6f52960541d5547498caa63f5e8a88ccaa88cca782e18d3d0872b632af2b15029a3e5d9923c4fa8d9e63278b68066f4b9b61430f6a780713d2f63a15246cbb335799e50b32d65f06c05cde8736dbc8c854a192dcfb6e47942cdb696c1b30d34db1aa1190363a15246cbb33d799e50b36d65f06c07cdb64568c6c058a894d1f2ec489e27d46c7b193c3b40b3ed119a3130162a65b43c3b93e70935db5106cf4ed06c4784660c8c854a192dcfaee47942cd7696c1b30b34db19a1190363a15246cbb33b799e50b35d65f0ec06cd764568c6c058a894d1f2ec499e27d46c77193c7b40b3dd119a49655c9d02c6f5296064d6b15029a3e5d9cbc4b3a70c9ebdc0733113cfde32782e069e2724cf13e6d4c565f010430e3eb73a058ceb53c0a83aaa8e921855c7ead15119955119cb63ec4f01a3ee6b6594cac8f0fdaae46f912e1ee7b19bbcd84d55123beeb748e33db6e6b9e67935c4d63cd73caf86d89ae79ae7d5105bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae795e0db135cf35cfab21b6e6b9e67935c4d63cd73caf86d89ae79ae7d5105bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae795e0db135cf35cfab21b6e6b9e67935c4d63cd73caf86d89ae79ae7d5105bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae792e29f6bee46317ca7dc6cc3ee0e178e60d533bf376bb97b86d3d9aa07e56ab4b3dad2ef6b4ca419d4b40bf4b19f4cb405cda36ad53bc72992f12c0cc14bb60fb9749d07e8ab1ded3c3c6bf8ca9ed717dfd65e33c765c5f3fde63c7f5f5e33db6e6b9e67935c4d63cd73caf86d89ae79ae7526263b93e18be6ea7e79fda6d3cd195ebdc3ab2929fea5c39a1f83a25d0638823b61e437aaea886d89ae79ae7d5105bf35cf3bc1a626b9e6b9e57436ccd73cdf36a88ad79ae795e0db135cf35cfab21b6e6b9bc3ccfc1fb3563c013783c41099ebdc278160ae3992d8c67aa309e75c2782609e3592a8ca75618cf0e613c5b85f1f408e3e914c65310c6b34818cf32613c7384f14c13c6b34118cf64613c75c278f2c278160be3992b8c672c7ecf500ecf74613ccb85f16485f16c14c6532f8c6795309e9dc278fa84f16c13c6b344184faf309e2e613cedc278360be36915c6334f18cf0c613ce709e36914c6d3208c67a5309eddc278d608e3992f8c67a6309e9c309e26613c1384f1ec11c6b34b18cf5a613cdb85f16c11c6d32d8c6793309e0e613c2b84f12c10c6334b18cf14613ccdc278260ae3c908e0c906673f932c0befef035f8df7597bbd3467c6f0fb973b7f0d7ce60a57ae8dd8f6e5e0a3df865f11f159d4e972684bbf2be71fdb12ea84b1fa619de23502c7154278260ae36916c6334518cf2c613c0b84f1ac10c6d3218c6793309e6e613c5b84f16c17c6b35618cf2e613c7b84f14c10c6d3248c27278c67a6309ef9c278d608e3d92d8c67a5309e06613c8dc278ce13c6334318cf3c613cadc278360be36917c6d3258ca75718cf12613cdb84f1f409e3d9298c6795309e7a613c1b85f16485f12c17c6335d18cf3e613c7385f12c16c69317c653278c67b2309e0dc278a609e399238c6799309e45c2780ac2783a85f1f408e3d92a8c6787309e5a613c4b85f14c12c6b34e18cf54613cb385f12c14c6b357184f4d040fc3ffbf0c79e8fe35da36adef13129b613f84fff7f34aa6365de5b655efb64bfc14af0eea1c741da9bddf0b3f4b5cfefd86f8ddfc2ad0e82aa6b6d0fec878fb873b36de57190043e0e91344f070dc8fcad4ce117998e0ff9fcd5badaef6b4f2f75d0eea5c09fa5dcda05f546ed3fad5c043e77162cd42bd754218c97719334f16da4c4ba963e06ae0e1382699da19e6ea355e9bd645e84e753057af616867d4b143ebd700cf065726d62cd4db2084917c5731f364a1cdb494cad56b8087e3d8616a6798abd77a6dda10a13bd5c15cbd96a19d51c70ead5f0b3c1b5d9958b3506fa31046f25dcdcbd3918536d3522a57af051e8e6387a99d61ae5ee7b5696384ee540773f53a8676461d3bb47e1dec076556e62866cb43bf2320d62cd4db2484917cd7b0f274e4b3d0665a4af563d7010f473fcfa47bd88f5defb5695384ee540773f57a8676461d3bb47e7d44ec9620592d6e1885163744f0dc30c65a50bc7299af4c21b3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3eaac3ac731abceaa731cb3049d2d0f3dc79358b3506fb31046f25dcbcb13fe2e68733072c978ebfd50be0178ae63d087a99de13de4fbbd366d8ed09deae0f1b59fa19d51c70eadef87fdb0bf0ce6eb53c8ac3a57c66c79e8ff67106b16ea6d11c248beeb7879c27e6c4b307229d58fed071e8e7e9ea99d613f76c06bd39608dda90e1e5f0718da1975ecd03ac55366658e63b63cf45c3662cd42bdad4218c977032b4f21fc7de3d660e452aa1f3b003cfb13e729f6630cba87fdd841af4d5b2374a73a98ab0719da1975ecd0fa41d80fe5305f9f4266d559758e63569d55e73866d559758e63569d55e73866d559758e63569d55e73866d559758e63569d55e73866d559758e63569dab4767cb43ffff9258b3506f9b1046f2ed67e5690fe71db60523978cb7de0fe583c07320719ee2bc0383eee1bcc321af4ddb2274a73a787c1d626867d4b143eb87603f8c77e6eb53c8acb93136cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc1272c3f26c776562cd42bded4218c9778097277ceec1f660e452eabe9d43c07390411fa67686f7ed1cf6dab43d4277aa83c7d7618676461d3bb47e18f683322b7314b3e5d9e1cac49a857a3b843092ef202f4fd88fed08462ea5fab1c3c0c3d1cf33b533ecc706bc36ed88d09dea60ae0e30b433ead8a1f501d80fcaaccc51cc9667a72b136b16eaed14c248be43bc3c613fb63318b994eac7068087a39f676a67d88f1df1dab4334277aa83b97a84a19d51c70ead1f81fda0ccca1cc56c7976b932b166a1de2e218ce43bcccc938536d352aa1f3b023c1cfd3c533bc37eec46af4dbb2274a73a98ab3732b433ead8a1f51b8167b72b136b16eaed16c248be01669e2cb4999652b97a23f0701c3b4ced0c73f5a8d7a6dd11ba531dccd5a30ced8c3a7668fd28f0ec716562cd42bd3d4218c9778499270b6da6a554ae1e051e8e6387a99d61ae1ef3dab4274277aa83b97a8ca19d51c70ead1f039ebdae4cac59a8b7570823f9b01fdbcbc4d3e8f1344668311e633779b19baa2476b317bbb94a626b9e6b9e57436ccd73cdf3a00a626b9e6b9e5743ec6acd35d5bc3a35cf9c43cd33e750f38c6a2e52f347938bdd990b86971a88c5303ed7369a3147e459288c679f309ed9c278a60ae399248c67a9309e5a613c3dc2783a85f11484f12c12c6b34c18cf1c613cd384f14c16c653278c272f8c67b1309eb9c278a60be3592e8c272b8ca75e18cf2a613c7dc2789608e3e915c6d3258ca75d184fab309e79c2786608e3394f184fa3309e06613c2b85f1ac11c6335f18cf4c613c39613c4dc2782608e3592b8ca75b184f87309e15c2781608e399258c678a309e66613c1385f16404f06483b37fdb81bf27a8051fdddfbf177c4f72e57de0ab898841db39063e1a3fa56dd8f3d58533ce66a881cfdc14c1f5a4887814e7a688cf8e85ee18ab1fd6295e2370dc248467a2309e66613c5384f1cc12c6b34018cf0a613c1dc278ba85f1ac15c63341184f93309e9c309e99c278e60be359238c67a5309e06613c8dc278ce13c6334318cf3c613cadc278da85f17409e3e915c6b344184f9f309e55c278ea85f16485f12c17c6335d18cf5c613c8b85f1e485f1d409e3992c8c679a309e39c2789609e359248ca7208ca753184f8f309e5a613c4b85f14c12c6335518cf6c613cfb84f12c14c65313c1b38f8927ee790afb04c4b6e3f0746d4a637459787f2c7e17b4cf63a4f5a3c0483ebc2f36cfc413f70c8abc80d8568b5550b64b16dec7df7570e554de63a4f5a89cc2fb1a5731f1c43db7639580d8560b9aaba07b00b2f03edec7cc9553ab3c465a8fcaa9665e9ef0ff07ac0c462ea5ee35c2638e631f32b5338fc75f82cfd0887c46f24a4fab1cd4198be748c4f507144f9995398ed9f2d05c13b1e2f96c2c7e47351ac6a8f32b034fd83fb606239752fde351e0e1387f30b533ecc78e7b6d6a8dd09dea60ae1e676867d4b143ebc72362b704c96a7162145a9c88e03931c65a50bc7299f7a5905982ce9687ee4524d62cd45b2184917c795e9eb07f5c118c5c4af58f278087e3fcc1d4ceb04f38e9b5694584ee54078faf930ced8c3a7668fd24ec8772988fa7905975ae8cd9f2d09c08b166a15e410823f98eb2f214f25968332da5fab193c0c3d1cf33e91ef663835e9b0a11ba531d3cbe0619da1975ecd0fa20ec076556666556666556666556666556666556666556666556666556666556666596cd6c79e8b7c3c49a857aed4218c9778295a738efd01e8c5c4acd3b0c02cfc9c4798af30e0cba87f30ea7bc36b547e84e7530574f31b433ead8a1f553b01f945999955999955999955999955999955999955999955999955999955999955936b3e5a167b6136b16ea75086124df495e9ef0775b1dc1c8a5d4bcc329e0e19897616a6738ef70b3d7a68e08dda90ee6eacd0ced8c3a7668fd66d80fcaaccc51cc96879e1d47ac59a8d72984917c83ac3cc5f9d3ce60e452aa1fbb197838fa7926ddc37eecb4d7a6ce08dda90ee6ea698676461d3bb47e1af64339ccc753c8ac3aabce71ccaab3ea1cc7ac3aabce71ccaab3ea1cc7ac3aabce71ccaab3ea1cc7ac3aabce71ccaab3ea1cc7ac3aabce71ccaa73f5e86c79e87f2c126b16ea75096124df29569ef670dea12b18b9949a77380d3c1cf3324cba87f30e67bc367545e84e75f0f83ac3d0cea86387d6cfc07e18efccc753c8acb93136cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc9a1bca1cc7acb9a1cc71cc1272c3f274bb32b166a15eb71046f2ddcccb133ef7a03b18b994ba6fe70cf09c66d087a99de17d3b435e9bba2374a73a787c0d31b433ead8a1f521d80fcaaccc51cc96a7c79589350bf57a843092ef34334f16da4c4ba97e6c087838fa79a67686fdd82d5e9b7a2274a73a98abb730b433ead8a1f55b80a7d79589350bf57a8530920fcf71bd4c3c8d1e4f638416e72ab6d5a2cf95cf73af5978bf0f18b9fa965e8f91d631c7c9d7083c7d4c3c4d1e4f538416e72ab6d5620d94ed9285f7d70023574ef5798cb41e95534dc0b38689a7d9e3698ed0e25cc5b65aac75e529ee350befaf0546ae9c5ae331d27a544e3503cf5a269eb83e69ed18c48e3bbec622765cae8c456cd55c3557cd55734ecd33e750f3cc39d43ca39a8bd29ce13a2a1c3ba5180130e0d20f65fcaec071edc9d4ce7cd4f7b1b55e9bf0fb188e399cabef1bcaaccc71cc4ce3161d592f36e913783cb40c316b319663907d5e9ba48d4196cb7c3c85ccaa7365cc36f6adc9c7eec87ab1499fc0e3a1e556662d98da19f607b705d11a53bc1cd4c13cbd8da19d19884bdba6f5db603f94c37c3c85ccaa7365cc36f6ed89c72e3e9b1d63933e81c743cbedcc5af0b4b3d81fdc11446b4cf1725007f3f40e867666202e6d9bd6ef80fda0cccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaacccb2996dec27271ebb387e8fb1499fc0e3a1e5c9cc5af0b4b3387e7f6710ad31c5cb411ddce77732b433037169dbb47e27ec076556666556666556666556666556666556666556666556666556666556666596cd6c633f25f9d8e1ef713036e913783cb43c85590ba67686e3f77705d11a53bc1cd4c17d7e17433b331097b64deb77c17e5066658e62b6b19f9a78ece27c1ec6267d028f8796a7326bc1d3ce627f707710ad31c5cb411ddce77733b433037169dbb47e37ec8772988fa7905975569de3985567d5398e5975569de3985567d5398e5975569de3985567d5398e5975569de3985567d5398e5975569de39855e7ead1d9c6be27f1d8ede1f83dc6267d028f87967b98b5e0696771fcfede205a638a97833a98a7f732b433037169dbb44ef1aa81f9780a993537c68659734399e398353794398e59734399e398353794398e59734399e398353794398e59734399e398353794398e59734399e398353794398e59734399e398353794398e59734399e398353794398e59426ed8d84f4b3e76f87b768c4dfa041e0f2d4f63d682a99de1fd2ff705d11a53bc1cd4c13cbd8fa19d19884bdba6f5fb603f28b3324731dbd8f733c4ce7ab1499fc0e3a1e57e662d98da19f6070f04d11a53bc1cd4c17dfe00433b331097b64deb0f406cd43ba1d8e13d9014a3c6bd5adfd35db9167ccf70e53af03dd395ebc1f72c576e00dfb35d7902f89e03ed21df735d7939f89ee7ca6bc1f77c575e03be17b8721ff85ee8cabde07b912b0f81efc5ae7c0bf85ee2cab782efa5ae7c1bf85ee6cab783efe5ae7c07f85ee1ca4f06df2b5df94ef0bdca959f02be57bbf25de07b8d2b3f157caf75e5bbc1f73a57be077caf77e57bc1f706575e0cbe07237c6f74e5a781ef4dae7c1ff8deeccafbc0f716579e04beb7baf264f0bd0dcaf4fa76573e0f7c0fb97223f8dee1ca39f0bdd3959bc0f72e579e02be77bb7233f8dee3ca53c1f75e579e06bef7b9f274f0bddf956780ef03ae3c137c1f74e559e0fb902bcf06df875d790ef83ee2ca73c1f751579e07be8fb9f27cf07ddc951780ef13aebc107c9f74e545e0fb942be3fefdb42bdf0f3eea571e001ff52b4f071ff52bcf001ff52bcf041ff52bcf021ff52bcf061ff52bcf011fe5dd73c14779f73cf051de3d1f7c94772f001fe5dd0bc14779f722f051debd187c94772f011fe5dd4bc14779f732f051debd1c7c9477af001fe5dd2bc14779f72af051debd1a7c9477af011fe5dd6bc14779f73af051debd1e7c94776f001fe5dd83e0a3bc7b23f828efde04be16577e33f8ce77e5b780ef02577e2bf81ee7cad8cf5ce8ca6f07dfe35df921f0515ff80ef05de4caef04df12577e17f896baf2bbc1b7cc95df03bee5aefc5ef0ad70e5f7816fa52bbf1f7cadaefc01f0ad72e50f822fefca1f025f9b2b7f187c0557fe08f8da5df9a3e0eb70e58f81afd3953f0ebe2e57fe04f8ba5df993e0eb71e54f818fcee3d4cfd8e3d91e83a40369647dd4e6d688b6906f22b4a53f48f69a8e62d1b669bd1d18691f14c69eb1305ac6368fd1f27432688679454ba9ef1f9dc0d3c1c0c3d4cef0fb4797d7a676af4d39a87311b4b38ba19d19884bdba6f52e88cdb1cf518b7ab7dd259e167550e73fdc49ce9e3b4be948dbb0f95b88684b2f735b68dbd42ff58e41ec6e2f76de8b8dfd312da58eaf6e60ee6160b6dbed4b7ebbe1f1b5da6d8b728ae2e4a14d6b4083a4da84b133ce280ef9eba05c3363b82ed5233de8fc45ec3697695f22bbffb94eef7339a8d31bd1fefe20d9f6f7793c7d1eb3dd27ff9c3eccc1703c8439d0eb71d07a1eb4eb8bd1ae17b4a33a78fe6b63d2aec7e3a1f536e0a16b9c2ef0d1b502f1e37556eb1870fbfd5e570437f9ba81b12d82319f3c6378add3e631d27a1e18c9d7033cdd4c9af9fb7a89a70f9e971bbc3af4d93aa833cbf525538291df3fa8ae3dee166786db45dfc11f0d92edd31b18f4c2f18100f4093c0d03d08bda59cfc03339181e2338333478fac08d03970c1c389c01b43a0f135f3311cda8011f966b237c4130722804876469280487646b3c59700886eadbaf52b65934dc3070e2d8d0134f0e9c3c74fa8e53430387770fde88d4f51e3d92c6b50049d147cbc46078d0a63f4836791abc58a5926722bc4e60e0616a6778d29be4b5a9c16b530eead4c37b9318da9981b8b46d5ac70160f26176d2fb386150e3b505b37812bcfafb36d10651c0c7c1f6330ecebe57e7846d70f0242ef5187614d15ec9d91d6147056d6f6d47fdec616747f5ec8591edc8ed289d1d95b3a37076d4cd8eb2d951353b8a6647cdec28991d156b098aa35e1700cf5780d18e6ad9512c3b6a65bf49dab3821d85b2a34e7694c95e4dd86f29f60c6caf38edd9d09e05edd58efd3665bf59db338c3d7bdb33933d9bd8ab237b5564af62ed559d9d9959676cbdd37a83b18dc63619db6c6c8bb1adc6b619db6e6c87b19dc67619db6d6c8fb1bdc62e36f684a038a27c89b14b8d5d66ec89c62e377685b12b8d5d65ec6a63d718bbd6d875c6ae377683b1fdc60e183b68ec90b1c3c6068c1d3176a3b1a3c68e197b92b19b82e21d1e278c9d343668ec94b19b8d9d36762628ce12d959213b0b64677dec2c8f9dd5b1b33876d6c6ced2d859193b0b63675dee098ab32a7676c4ce86d891703bf26d47baedc8f63383e2c8b51da97e4e501c89b623cf76a4d98e2cdb91643b726c478aedc8b01d09b623bf76a4d78eecda915c3b726b476aedc8ac1d89b523af76a4d58eac3e1814474eed48a91d19b523a176e4d38e74da91cd8782e2c8a51da9b423937624d28e3cda91463bb2684712edc8a11d29b423837624d08efcd9913e3bb26747f2ecc89d1da9b323737624ee33c61e36f659639f33f679635f30f645635f32f6e5a098935f35f635635f37f60d63df34f62d63df36f61d63df35f63d63df37f603633f34f623633f36f688b19f18fba9b19f19fbb9b15f18fba5b15f19fbb5b1df18fbadb1df19fbbdb13f18fba3b13f19fbb3b1bf18fb6b303c6a8f1dc53fdd0a8d201f181a1a38716aa86568b0e5c42dc7878e9d3a7e47cb6dc7868eb60cde3a70fac8f1c1dbf0c36f775d130d8f6f387dfac01d2dc74e1e1eb8bd65f096a196c1232d07076f3979f80c7ee8cbee43f3cf8e78e0f0e1f8603fae790ca43fab30e89fdde768e26147e9b6fdad1241feb3920f4dabadac4157b8330b7d2bbdb47815d772e6f8e0504bbee5a4f97be0b8f9ccc0e1d6167cef8c11f9cc50cb99a103a7875a8e9c1e3cd1d2d68adb3d30a98246fc637a051f9a3963f42d0ffe1f68e5b57eb1020400", + "packedBytecode": "0x000000028df71de50000003d361f8b08000000000000ffed9d09941545bee6b38a62d16b95b82bb8948a82acb72ec58e50b8e0ae08222020140508c84eb183b28328822cb243b1ef8be0d26def76b7bda96d2f6adbfdba5f77bff7fabd7ef3ce9cf3e6cc3b33f366c69e8c7be33ff511449675cb8cf2bb12794ed48dfc2a32e3175f46462e9199911764a6bf86214fc71b84a14570fe24ff2fd3bfc92f3695c4b8aea44bcebc1ce1cccf11ce0639c25990239c0d7384b3518e7036ce11ce2639c279518e705e9c239c891ce1bc2447380b7384b32847382fcd11cea639c279598e705e9e239c57e408e79539c279558e705e9d239cd7e408e7b539c2795d8e7036cb11cee639c2797d8e70de90239c37e608e74d39c2599c239c37e708e72d39c2796b8c9c6d8053eed9dfa67f6fd7bf2df56f2bfd7b87fe6dad7fdbe83216e8f9b661681786f661e860fc4f19a36edca7c2d0d1f85f69183a85a17318bae8ff15ebff750d43b730740f438f30f40cc39d61e81586deda8f3e61b82b0c7787e19e30dc1b86be61b82f0cf787e181303c188687c2f070181e09c3a361782c0cfdc2f07818fa876140189e08c3c0303c19864106cbe0300c09c35361181a866161181e86a7c330220c23c3501e865161a808c3e8308c09c3d8303c13867161181f86096178360c13c330290c93c330250c53c3302d0cd3c330230c95619819865961986d7836270c73c3302f0cf30dce0561581886e7c2f07c1816856171189684616918968561791856846165185685e18530ac0ec38b6178290c6bc2f07218d686615d185e09c3fa306c08c3c6306c0ac3ab61d81c862d61d81a866d61d8ae596447d811869d61d81586dd61a80ac39e30ec0dc3be30ec0fc381301c0cc3a1301c0ec391301c0dc3b1301c0fc389309c0cc3a9309c0ec36b61381386b361783d0c6f84e1cd30bc1586af85e1eb61783b0cdf08c337c3f0ad307c3b0cdf09c377c3f0bd30bc1386ef87e10761f86118de0dc38fc2f0e330fc240c3f0dc3cf0ccfdf0bc3fb61f8200c3fd7da87faf717faf797faf757faf7d7faf723fdfbb1fefd44fffe46ff7eaa7f7fab7f7fa77fff4efffe5efffe41fffebdfefda3fefd93fefdb3fefd07fdfb8ffaf79ff4ef5ff4ef3febdf7fd1bfaa1f6f75f34cbc49503d950531b539a563cad57d7c31d3ec27547d500df4ffe4b758eb057a5e7ef3b4de50cf3734f4467abe91b19e267abe89a117e9f922436faae79b1afae57afe7243bf52cf5f69e837ebf99b414fe8ff650a96f9515a032de58126f52f1fb4865a6b005a23591d688db5d61034d9be8d40bb486b8d41bb586b4d404b68ed22d02ed1dac5a0156a2d015a91d62e01ed52ad1582d6546b45a05da6b54b41bb5c6b4d41bb426b978176a5d62e07ed2aad5d01dad55abb12b46bb4761568d76aed6ad0aed3da35a035d3dab5a0e95d2cb80eb4ebb5d60cb41bb4d61cb41bb5763d683769ed06d08ab5762368376bed26d06ed15a3168b76aed66d05a68ed16d0e45ce156ada97a266d5f7a19ade783769becdfa0dd2efb36682d65bf06ad95ecd3a0dd01798bd65af671d0da684deab8fa5f671d2f0be26adf52156abd5de25e6fb866b5de6ef1af37a9dacbee41b5d765904f17f0aa878ec7f83c4309e69da783e4237a01c4ef83b4924efc90764ad855dbd355c77bd4b05c6763b92248d3d552feb220def2773378ba19cc6a9bf4048ef8eb6cc7a4afb3b59eb2aeb30321ad59f7e498f955acb30f0287833a5beaeb6cada7aceb6c05a435eb9e9cbf7d15ebec60e0705067bbbaa9b3a9a4afb3997b264160af7b722df155acb3638123fe3adbc9d7d9da4f59d7d9e721ad59f7e4baf6ab58672b8123fe3adba5ab3f37a8f594759d5d0369cdba27f758be8a7576097038a8b315be9dadf594759ddd0e69cdba27f7fbbe8a75761d70c45f67bb39aab31d7d9d0d32fd5f4160af7b72eff9ab5867770147fc75b6c2df9fadfd94759d7d0bd29a754ffa41be8a75f6b88eab7e860f753fc3f5a0fd426b3780f64baddd08daafb4761368bf86fe42d13e92be44d03ed6da2da07da2b55b41fb8dd1f7aab44fb5761b68bfd5daeda0fd4e6b2d41fb3badb502edf75abb03b43f68ad35687fafb536a0fd516b6d41fb93d6da81f667adb507ed1fb4d601b47fd45a12b47fd25a09687fd15a0ab47fd65a47d0fe456ba5a0fd556b9db4a6fa29a5efeb5dad3501aeb220be3a9a000f64ca33e6cb20dec12d4fb2087830af92f8f3eaa8ca7ecec3569f53f612e04939287b02f2a80d4f0a783ac6cf937ec7af34fef5a6b771d2f034017925a15c9d1c942b0ff29275cbbce457041aeed39d2c8cb19f6784175e799097ac5be63b03a368d8c6489b2bfb8f3ac6fc2bf03ad897d2e719929fbcbb2c1c25a04b9ae2e6d56cff556b85f07f6c6f3b1a9aa37a99ae179297ac5be64b8151cad3b1fe1953b5654c198caeda883cc84bd6edf3aede0e126f0f3c5d1df1986d9ae4ddb51ef2ee6ce45d62e48d6da74c351ddb3a03b383ebad922fe37a0bcf1f18aeb72ecbab4e6b5e37493b9cedf556276339c6ebad4679d51c0ef687741de86a70c87c0978d72dc2bbaee09da4b90dbc7370ee99f6ae8bc123f329e09176ac1478dad7334f7b82bcf11c56b61b5e4be17980abedd5de609479dbf6ea0c8c290ba38373c2544dc7c81260140def47251d7916b55d932479bbb8aec47729e4dcdcbcb6298034ff273ff3abdaca76794ed95278ee2c536daf79e3df4ea924ee1fb5e171bc0f9538aa8f49bc7ff359106f5d33db25b3bd89bac7e3aa2d4f1a3c322ff97966cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367e667563cd29f80cf6d49ba8e248ce6f36faeeef3a7bf8522ebd2bfaa5fe7dbd0af137fbf452a897d7ff21c444ba3cc0590e6c3bceab4efe8b87ae640faa9f0592bf3f92b977d76f83c6619cc4b7ef8ac156e4b86e79d8a63cb3bf35d1a17fd6d9706996fea98cf7e76b478eae2f967f434cff0149f03bfc3e051f5f46ff9d56c2efafeb2ed8b44af241e67df5e5160afebf16f97d4397dd6f9c1b9ed071e675c3dbf236db5f4977735f22e80347f80e7a8e4d92a196728692c87cffdc8ba659996a07733d6dd542f2b1c0d8df597c2b292e64fd0a62e83baeaa0ad4c65fbec3af69bc77f1ccef4e327b3e0690f3c2eda1947e71b49dc07e2eec7379f4fb39dc7481a7cb6cfc17395353eef24f97966cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367e667563c665f2bbe5f5b42c2289ae3671fd2fd19adf4ba64fdaa5f677a7e75beaefbe1a4cfa99551667c77f46c7e75da593a5e189cffbc43d4b674f57d8aa86d29f9e1b767da038f8b7edd3cc84bd6ddc1e285c48b63cb3bd38f1fbfc7d5fdf849c3d7128ba7aef657ec63454f717f4d193cd8371af56d9f0e86e6681fafb15e487eb82f75004de2f87eb48bed8cc712f3b91ec90ffbaf5fd6de360d5c6dfb54d265bb81df4d290bceafdff83d950dd0f66dd2717c86039f1dd96ff9bf4c35f5538b7faacc3de32f737afbde099c65900fe6dd0b5863cabb04f3ced341f211bd00e2fbf2abd34a3af143bc1676b58fc8376090dd5c2e652c5704697a58ca5f16c45bfe9e064f4f83596d932d50cff6c3f1df559bd423c2a396e091a4c1f32057cfe4996da4f97c233eb7d7c84883e72c92e638b451aa2cb6e7476dcf1cba3a8e453d73683b376e0f8c6619cde73c2ff4e7b4be09ed45dccf697d13ea103ea71518eb6f05eb17ae4641f4b145d27cd758bf794e2ecbe0736092e6fbd05efc471dcec9bfaceb2bdb39392e175576c58cdf412b8b9119eb04b2e07982a479dfd866dd22b83b5b96fd306259f1cafc56586170be7f6ebeb396d9e77b1a65917add03ca22693e36ce01e33f6fc99c03bafaa61c9e7ba8f576b09455d2fc0ef6b5dfc3399e6c27dc6fffcdf27f996a3a07c4f11c7bc75fe624ee33b27d7b5bf2ee03ac31e55d82eb9273c03e86070510ff2fd0a64b3a492b5e0bbbda47e43c0ad9cde5ba1acb15419a5e96f29705f196bfb7c1d3db6056dbe48f50cffe0dce015db5d5bd223c6a051e491abca768fb0ea8ed18e3ead9fea8634c076034db4d3c4eba6433df1332efabd9ce11248d2c8be708ff09ed6cc292d6bc5f28c7cb389f1bc677254a21dfbf59f22d8bd9cf2ee05b19cce379c19799b783ef84a7df11c1ef0a0a034e6510c76f87cb72f9c0e8e27beb89e0dcef037f1e237e835d966b008c2eb661b6df272e054659ae00185dbcff85dfc8af0d634760c47d51185d7c97b4aedf73c4ebee46c0c8f45e15de3f6e0c8c2eee73d4f57d1a3ce636815f57638724b360c463ae2c771130bab87f85f7ef6bc388e72eb2dcc5c0e8e23e73b663b0e0f7a1f1fe8f30ba3807ccf61a05ef55c97297006377478cddb260ec0e8c78cf41185d5c4f2620dfda30f60046e41546077d0069c61e5930e2bd7259ee5260bcd31163cf2c18ef044659ae2930bab89f9f807c6bc3d80b1865b9cb80d1c1b57e9ab157168c784d2ccb5d0e8c658e187b67c158068cb2dc15c0e8e2ba3d61307c1e631f6014fd4a60bccb11639f2c18ef024659ee2a60bcdb11e35d5930de0d8cb2dcd5c0788f23c6bbb360bc071865b96b80f15e478cf764c1782f30ca72d702635f478cf766c1d8171865b9eb80f13e478c7db360bc0f1865b966c078bf23c6fbb260bc1f1865b9e6c0f88023c6fbb3607c001865b9eb81f141478c0f64c1f82030ca723700e3438e181fcc82f1216094e56e04c6871d313e9405e3c3c028cbdd048c8f38627c380bc6478051962b06c6471d313e9205e3a3c028cbdd0c8c8f39627c340bc6c7805196bb0518fb39627c2c0bc67ec028cbdd0a8c8f3b62ec9705e3e3c0d8cfc2d8df11e3e35930f6074659ee0e601c103f63fa3b34fdb3601c003c4fc4cf93f66c40163c4fb8e5497fc3698025af27e3cf2bbd2d0606b52ffb93c033287e9ef4b678320b1e612882e5d0b3c1f133a63d1b9405e360e019123f4fdab3c159f00c01cf065b3c7b2a7ec6b46743b2607c0a7886c6cf93f6eca92c788682674f593c1b163f63dab3a159300e039ee1f1f3a43d1b9605cf70f06c98c5b3a7e3674c7b363c0bc6a7816744fc3c69cf9ece82670478f6b4c5b391f133a63d1b9105e348e0298f9f27edd9c82c78cac1b39116cf46c5cf98f6ac3c0bc651c053113f4fdab35159f0548067a32c9e8d8e9f31ed5945168ca381674cfc3c69cf4667c133063c1b6df16cac23c63159308eb5f0c4fd7dd13196bcc6392afb3341edcb2e0c45b01c328e77c4382e0bc6f1c028cb61fffa04478ce3b3609c008cb21cf6af3feb887142168ccf02a32c87fdeb131d313e9b05e3446094e5b07f7d9223c68959304e0246590efbd7273b629c9405e36460445e619ce2887172168c53804b96c3fef5a98e18a764c13815186539ec5f9fe688716a168cd3805196c3fef5e98e18a765c1381d186539ec5f9fe188717a168c33805196c3fef54a478c33b260ac0446590efbd7673a62accc82712630ca72d8bf3ecb11e3cc2c186701a32c87fdebb31d31ceca82713630ca72d8bf3ec711e3ec2c18e700a32c87fdeb731d31cec982712e30ca72d8bf3ecf11e3dc2c18e701a32c87fdebf31d31cecb82713e30ca72d8bfbec011e3fc2c181700a32c87fdeb0b1d312ec882712130ca7278cdf59c23c68559303e078c0b3de305c1883cc5f1f124b1ec98d7f304657fdec29317b8293be6b588a0ec8bf46fae313e97038c637380d1fbe8193de3578fd1efd7de472646efa3f7d1337a46cfe8192f74c65c68c33d634ed4c7545d1915cfe2f879d29e2dca8267317826cb3d91038ccfe700e3736e19537565543c4be2e7497bb6380b9e25e0992cf7845bc6545d1915cfd2f879d29e2dc982672978b6c4e29903c6545d1915cfb2f879d29e2dcd82671978b6d4e29903c6545d1915cff2f879d29e2dcb82673978b6cce29903c6545d1915cf8af879d29e2dcf82670578b6dce29903c6545d1915cfcaf879d29eadc882672578b6c2e29903c6545d1915cfaaf879d29eadcc82671578b6d2e29903c6545d1915cf0bf1f3a43d5b9505cf0be0d92a8b67ac8c637380f1b91c60cc051f3da3676462f4fbb5f79189d1fbe87df48c9ed133e62ee3f339c0e8b7b56764655c1d3f632a5bc6d58e3d7354cef4385e2fea75c5f86d8994f2ea25c3ab5586574590e645f0ef2507fee541beb26e9997fcb2656e41c0ec28ef948c2fbdcac8ef39c30f35add1bf0d411f0b9ebeace332869fa47f1e744933570f98226383caf42c94774dfce52da969df96fc90673419cf22329ea7c9789692f10c26e35941c6f33819cf43643cf792f1f426e36943c633878ca72519cf74329e14194f03329eb6643cddc8784690f18c21e31942c633918ca73f19cfc3643cadc878fa92f19491f17427e3994bc65342c633838ca70b19cf24329e67c8781693f18c24e35948c6f31419cf32329e01643c2bc9781e21e3b98f8ca70719cf3c329e24194f25194f67329e3e643ce3c8782693f19493f10c25e31948c6f328194f0b329efbc978ee22e3e949c6339f8ca70319cf4c329e4e643c53c878c693f12c21e31945c6b39c8c671819cfed643c4f92f13c46c6731b19cf03643c7793f1dc49c6b3808ca71d19cf2c329e52329ef6643c13c878a692f15490f10c27e31944c6d38f8ce741329ed5643cf790f1f422e3e94ac6339b8ca73519cf34329e8e643c79043c89e0fc775213f0ffd5a0e51bcb360e4345f3eaffafd57a3e2cb34ec71b58d6bd16b497757c9d6559f4692d94a54cc7935f6c4afb847995c1bce457081ceb48783a92f14c23e3694dc6339b8ca72b194f2f329e7bc8785693f13c48c6d38f8c671019cf70329e0a329ea9643c13c878da93f19492f1cc22e36947c6b3808ce74e329ebbc9781e20e3b98d8ce731329e27c9786e27e31946c6b39c8c671419cf12329ef1643c53c8783a91f1cc24e3e940c6339f8ca72719cf5d643cf793f1b420e379948c672019cf50329e72329ec9643ce3c878fa90f17426e3a924e34992f1cc23e3e941c6731f19cf23643c2bc9780690f12c23e3798a8c672119cf48329ec5643ccf90f14c22e3e942c633838ca7848c672e194f77329e32329ebe643cadc8781e26e3e94fc633918c670819cf18329e11643cddc878da92f13420e34991f14c27e36949c633878ca70d194f6f329e7bc9781e22e3799c8c670519cf60329ea5643c4f93f12c22e3194dc6f3ac85c7c1f88e691e793f4fd62df3ab49f276b01dd2e35abee2a84cebf5ba1aeaf50abfe45700693e699cf96d1a64ea83e8c2b546c7b16e8837ebc1a3f58eca22db23cfd83e8ef34ee17ba3013004863f8185c7c5fbb68eca794e3d8c7b7cd50d8657e6b62b8234af807f1b1cf867abdb322ff965cbdc828019eb4571106fbdd8187f99feffb8ade2eb46c35f2cd7ab3a1e63bd4caa756cd2eb6a08f9bd0af96ed1f1bc18f355ebdaacd725e3cc0ac716d025cd7f83761999d524ed2f7e636dab8e97c5c79c1e071bc7d1154f702a83f856e0d9ec80c75139d3db669b51a6d546998a20cd2628e73607e5cc837c65dd32bf0d7864c2b1875dd483da6cf3a8b18719781691f13c4dc6b3948c673019cf0a329ec7c9781e22e3b9978ca737194f1b329e39643c2dc978a693f1a4c8781a90f1b425e3e946c633828c670c19cf10329e89643cfdc9783691f13c4cc6d38a8ca72f194f19194f77329eb9643c25643c33c878ba90f14c22e379868c673119cf48329e85643c4f91f12c23e31940c6b3928ce711329efbc8787a90f1cc23e34992f15492f17426e3e943c6338e8c6732194f3919cf50329e81643c8f92f1b420e3b99f8ce72e329e9e643cf3c9783a90f1cc24e3e944c633858c673c19cf12329e51643ccbc9788691f1dc4ec6f32419cf63643c1bc8781e20e3b99b8ce74e329e05643cedc8786691f19492f1b427e39940c633958ca7828c673819cf20329e7e643c0f92f1dc43c6d38b8ca72b19cf6c329ed6643cd3c8783a92f1e411f0448d3d2cffdf009abc5387e3116fd7f1cda0e55bf29067cfb78156a0e3b20ef52ae7e4e6e7af1b7d72f51e21e65506f3921f8e3dbc9d84a72319cf34329ed6643cb3c978ba92f1f422e3b9878ce741329e7e643c83c8788693f15490f14c25e39940c6d39e8ca7948c6716194f3b329e05643c7792f1dc4dc6f30019cf06329ec7c8789e24e3b99d8c671819cf72329e51643c4bc878c693f14c21e3e944c633938ca70319cf7c329e9e643c7791f1dc4fc6d3828ce751329e81643c43c978cac9782693f18c23e3e943c6d3998ca7928c2749c6338f8ca70719cf7d643c8f90f1ac24e31940c6b38c8ce729329e85643c23c9781693f13c43c633898ca70b19cf0c329e12329eb9643cddc978cac878fa92f1b422e379988c6713194f7f329e89643c43c878c690f18c20e3e946c6d3968ca701194f8a8c673a194f4b329e39643c6dc8787a93f1dc4bc6f31019cfe3643c2bc8780693f12c25e3799a8c671119cf68329e67eb87a754bd8b87e37d0a174e6510df063c9b1cf8e3a89c497c0f33ceb13495573b0caf36185e15419aade0df0e07fee541beb26e9997fc3cb3678e62563cd2b7637b3f782109a3689b9cf29426135066996a6a1f77008f8be38723dfd3edd84ea34c0b2dbe4b1aacab3b1d94d3b6efc8fc4e4bdec541bc5eecaa8517bb2c3cbbead90bc92f5be6ad39c8ec7df63e47317b9fbdcf51ccde67ef7314b3f7d9fb1cc5ec7df63e47317b9fbdcf51ccdee7fa61f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcf7563563c8b745c5813906e1109a368dbdcf2a4df375a149c3be519f36510df053c3b1cf8e3a89ce967c8771b655a149cefbba4c1fd6bb78372daf61d99df0ddb211be69d39c8ec7dae1bb3e259ace38bf46f02d22d2661146d875b9e743bb6383877aaa91ddb0d3c2eda7947e54cb763554699165b7c9734b87f553928a76ddf91f92ad80e559ed9335b9815cf121d17d604a45b42c228da2ea73ca9f4fb8d4b8273a79adab12ae071d1ce3bf23ddd8eed31cab4c4e2bba4c1babac741396dfb8ecc4b7ed932efcc4166efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fb7ce1f8ac78e49bc0c29a80744b491845dbed94a763badf616970ee9467cc97417c0ff054c5ce93e97770e07bbadf61af51a6a516df250dee5f7b1d94d3b6efc8fc5ed80e5f75e69d39c8eceb46fd30fbbae199a3987dddf0cc51ccbe6e78e628665f373c7314b3af1b9e398ad9d70dcf1cc5eceb86678e62f675c3334731fbbae199a3987dddf0cc51ccbe6e78e628665f373c7314b3af1b9e398ad9d70dcf1cc5cc503714cf321d17d604a45b46c2285a955b9ef4770f9605e74e79c67c19c4f702cf1e07fe382a67fab99d7d469996597c9734b87fed73504edbbe23f3fb603b7866cf6c63563ccb755c5813906e3909a3687bdcf2a4dbb1e5c1b9534dedd83ee071d1ce3b2a67ba1ddb6f9469b9c57749837575bf8372daf61d99df0fdbc1337b661bb3e259a1e3c29a80742b481845dbeb9627dd8ead08ce9d6a6ac7f6038f8b76de5139d3edd801a34c2b2cbe4b1aacab071c94d3b6efc8fc01d80e9ed933db9815cf4a1d17d604a45b49c228da3eb73ce9766c6570ee54533b7600785cb4f38eca996ec70e1a655a69f15dd2605d3de8a09cb67d47e60fc276f0cc9ed9c6ac78faebb8b026205d7f1246d1f0fce750fc3ca50983474d35b563871cfbe3a89ce976ec7060f7fd10f82e69b0ae1e7650ce3cc857d62df387613b64c3bc330799bdcf7563563c03755c5813906e2009a3680781e748fc3ca50983474d35b563471cfbe3a89ce976ec6860f7fd08f82e6970ff3aeaa09c7990afac5be68fc276c88679670e327b9febc6ac7806e9b8b02620dd201246d10e03cfb1d87932df37461e35d5d48e1d73ec8f9b7266dab1e381ddf763e0bba4c1fdebb88372e641beb26e993f0edbc1337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993d3337b3e219a2e3c29a8074434818453b0a3c2762e7c9f43b208f9a6aea7738e1d81f37e5ccf43b9c0cecbe9f00df250dd6d5930eca9907f9caba65fe246c07cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6ccdccc8a67a88e0b6b02d20d256114ed38f09c8a9fa73461f0a8a9a67e87538efd7154ce74bfc3e9c0eefb29f05dd2605d3deda09c7990afac5be64fc376f0cc9ed9c6ac7886ebb8b02620dd701246d14e02cf6bb1f364fa4f91474d35b563af39f6c74d3933edd899c0eefb6be0bba4c1ba7ac64139f3205f59b7cc9f81ed900df3ce1c64f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcfdee72866efb3f7398ad9fbec7d8e62f63e7b9fa398bdcf178ecf8a67848e0b6b02d28d206114ed34f09c8d9da7633261f0a8a9a67e87b38efd7153ce4cbfc3eb81ddf7b3e0bba4c1fdeb7507e5cc837c65dd32ff3a6c87af3af3ce1c64f675a37e987dddf0cc51ccbe6e78e628665f373c7314b3af1b9e398ad9d70dcf1cc5eceb86678e62f675c3334731fbbae199a3987dddf0cc51ccbe6e78e628665f373c7314b3af1b9e398ad9d70dcf1cc5eceb86678e6266a81b8aa75cc7853501e9ca4918453b033c6fc4cf539a3078d454d3733b6f38f6c75139d3cfedbc19d87d7f037c9734b87fbde9a09c7990afac5be6df84ede0993db38d59f154e8b8b026205d0509a368afbbe549250c1e35d5d48ebd093c5f8b9f27ddaebe9905cfd780e7adf8794a1c9533a9d6fb75608f6bbdcaabb70dafde34bc2a8234c8f0b603fff2205f59b7cc4b7e9ed9334731635b28ac0948f70609a3686f018f8b764395bdb55e97acbf61180637adcef76cfcf996e2354b43bd5ee190fc0a20cdfdcdaad30ed36c85f07fd96eaa3c670ccdd1b76f4a6cef4bc8bce45718d4db35448dd734e8858be7fcb33deea31712ff2c88af5ee37e8e79b9f8b652b6ef5c9cb6f0c458f692a8f74d5c7cc74e95bd8d5e97ac5feda3539b3af5bc14f73d693fda18652e80342da0fda884f6c3d656b8de37f302fbbe991f54b767c255ac75f3dec4675a977467213db639fdf52fee9ffda1acaedac5a86b126c17cdb6dba5f7e6fd3133ef22f0e52ca967b6eb38f471a0857b200137d6c7fadccf64ddb67b08030d1fd93cc36d7dd6e2e3200bf720026ec6fd7a90e1239b679fb75f0fb1700f21e066dcaf87183eb279f679fbf5500bf750026ec6fd7aa8e1239b679fb75f0fb7700f27e066dcaf871b3eb279f679fbf5080bf708026ec6fd7a84e1239b679fb75f975bb8cb09b819f7eb72c34736cf3e6fbfaeb07057107033eed7b5ed2765daaf1dddff4ef793a2676aaae99ee159a7fe64be579fcdf327af018f8b3ae5a81e241ddd734df7939ae30f9c35bcc2f1071cdf97adf1bb44929f67f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b667e667cbf17fb5724ddeb248ca2397e363fdd9fd156af4bd6af9efd9d7d99cb7c5349ecf794e7924d8e0248f3efd756a79dafd9b07f11fb454f1b9acb6d698e3f2df3925f219407ebbfab77b5a3ead6eb96bc8b63cb3b55e1c6e354f2d2701d1705d5dbf994511ef4f464ecf99feb699ee1e949c77967fbbe04f238e88b2d7154ce745b70c22893e97111a46901e53ce1a09c7990afac5be64f008f4cf9c0e3aa0e06064f60f147a6d1643c83c9781e22e3b9998ca73719cf55643c73c87852643c6dc8782e22e39948c633828ca73f194f5f329eebc978ba93f13425e39941c6d3858ca7808ce719329ea7c8781e21e3b9958ca70f19cf1d643cd790f1cc23e34992f124c8782693f19493f10c24e3b99f8ce746329e9e643c9793f1cc24e3e944c6d3888c673c19cf30329edbc9781e23e3b98d8ce76e329eebc8781690f1b427e32924e3994ac65341c633888ce741329e62329e5e643c5792f1cc26e3e948c6d3848ce759329ea7c9781e27e3b9978ca739194f37329e96643c9792f14c27e36940c633868ca72d19cf10329e87c9785a91f1dc42c65346c6733519cf5c329e12329efa7827201b9e8bc9782691f18c24e31940c6731f19cf0d643c3dc8782e23e3a924e3e94cc6d3908c671c19cf50329e47c9785a90f1dc45c6732d19cf7c329e0e643c9790f14c21e31945c673868ce749329e07c8786e22e3b9938ce70a329e59643ca5643c8dc9782690f1b423e3194ec6d38f8ce71e329e66643c5dc9788ac878a691f1b426e3c923e04904e77f4b2701ff7f0d34f9e60b7e2f2cdfb23ee9a796f4eab838a7f9f9ebceb7acfba485017d3a0e6529d3f1e4179bcef9ce4d9e5eafcc4b7e85c0719284a73519cf34329e22329eae643ccdc878ee21e3e947c6339c8ca71d19cf04329ec6643ca5643cb3c878ae20e3b9938ce726329e07c8789e24e33943c6338a8c670a19cf25643c1dc878e693f15c4bc67317194f0b329e47c9788692f18c23e36948c6d3998ca7928ce732329e1e643c3790f1dc47c633808c672419cf24329e8bc9784e93f19490f1cc25e3b99a8ca78c8ce716329e56643c0f93f10c21e3694bc633868ca70119cf74329e4bc9785a92f17423e3694ec6732f19cfe3643c4f93f13c4bc6d3848ca72319cf6c329e2bc9787a91f11493f13c48c633888ca7828c672a194f21194f7b329e05643cd791f1dc4dc6731b19cf63643cb793f10c23e3194fc6d3888ca71319cf4c329ecbc9787a92f1dc48c6733f19cf40329e72329ec9643c09329e2419cf3c329e6bc878ee20e3e943c6732b19cf23643c4f91f13c43c65340c6d3858c6706194f53329eee643cd793f1f425e3e94fc633828c672219cf45643c6dc87852643c73c878ae22e3e94dc6733319cf43643c83c9784693f1e41b3cf87ff56e8f3caf7d42ff16c0ff97e987099aea75491a3946ab7b27c70c4d95f7a8a3f21e0baaa732983f0ae515f663c073cc11cf7183c7ccbb10e203c1b32386a6180f3b623c6230cafc616014ff8e00cf11473c470d1e33ef42880f02cf0e199a623ce888f190c128f3078151fc3b043c871cf11c3678ccbc0b213e043c3b60688a71bf23c60306a3ccef0746f1ef00f01c70c473d0e031f32e84f850f06c9fa129c6bd8e18f7198c32bf1718c5bf7dc0b3cf11cf7e83c7ccbb10e2c3c1b33d86a618ab1c31ee311865be0a18c5bf3dc0b3c711cf5e83c7ccbb10e223c0b3dd86a618773962dc6d30cafc2e60acd2f1ddc0b3db114f95c163e65d08f172f06ca7a129c61d8e18771a8c32bf0318c5bf9dc0b3d311cf2e83c7ccbb10e215e0d97643538cdb1c316e3718657e1b308a7fdb8167bb239e1d068f997721c4fb8126bc5d40dbaae39d41dba2e39d40dbace3a5a0bdaae31d41dba4e329d036ea7809681bc40fd0d6eb7807d05ed1f1f6a0add3f1aea0add5f16ea0bdace3dd415ba3e33d407b49c77b82f6a28edf09da6a1def05da0b3ade1bb4553a5e06da4a1def03da0a1dbf0bb4e53a7e3768cb74fc1ed096eaf8bda02dd1f1bea02dd6f1fb405ba4e3f783f6bc8e3f00da733afe20680b75fc21d0c6eaf8c3a03da1e38f80f6868e3f0ada9b3afe18686fe9f8e3a07d4dc70780f6751d7f12b4b7757c3068dfd0f1a740fba68e0f03ed5b3afe3468dfd6f191a07d47c74781f65d1d1f0ddaf7747c0c68efe8f833a07d5fc7c781f6031d1f0fda0f757c0268efeaf8b3a0fd48c72782f6631d9f04da4f747c32683fd5f129a0fd4cc7a782f69e8e4f03ed7d1d9f0eda073a3e03b49feb7825681feaf84cd07ea1e3b340fba58ecf06ed573a3e07b45febf85cd03ed2f179a07dace3f341fb44c71780f61b1dc7ef68ff56c78b8378dbf74f83eaa918f296fc549adfe97823238d2c5b00690af5cd4875bfa328a86effe578a03469ffb78226edff16d0a4fddf0c9ab4ffaf8226edff26d0a4fddf089ab4ff1b4093f67f3d68d2febf029ab4ffeb4093f67f2d68d2febf0c9ab4ff6b4093f6ff25d0a4fd7f113469ff578326edff0ba095e9f82ad0a4fd5f099ab4ff2b4093f67f3968d2fe2f034ddaffa5a049fbbf043469ff178326edff22d0a4fd7f1e3469ff9f034ddaff85a049fb3f163469ff9f004ddaff374093f6ff4dd0645ffb14343926bc059a1c13be069a1c13be0e9a1c13de064d8e09df004d8e09df044d8e09df026da48e7f1b3439267c073439267c173439267c0f343926bc039a1c13be0f9a1c137e009a1c137e089a1c13de054d8e093f024d8e093f066db28eff04343926fc14343926fc0c343926bc079a1c13de074d8e091f8026c7849f8326c7840f419363c22f409363c22f419363c2af409363c2af419363c247a0c931e163d0e49820c78826a0c9fd6a3525bfe05414544ff99097309505f11e73702a83f87c28bb4c8bc978d690f16c25e3b9998ce72a329e36643c1791f18c20e35941c6d39f8c673d19cf6e329e5d643cd793f11c23e3394ac6d3948ca7808ce745329ecd643cb792f1dc41c6730d194f828ca79c8c671919cf40329e75643c3bc9787690f1dc48c673848ce73019cfe5643c8dc8785e20e3b99d8c671319cf6d643cd791f11492f15490f12c21e31944c6f33219cf27643cdbc8788ac9780e91f11c24e3b9928ca70919cf4a329e0d643ccdc9785a92f15c4ac6d3808ca72d19cf22329e21643c2f91f17c4ac6d38a8ce716329e2d643c07c878f693f15c4dc6739a8ce762329eed643c6f92f12c27e379858ce706329ecbc8781a92f10c25e3594dc6f32a19cf3e329ebd643cd792f15c42c6f31619cf52329e33643c6bc9786e22e3b9828ca731194f3b329ee1643cabc8783692f1ec21e3a922e36946c673828ce738194f11194f6b329e3c029e047004a0c9ff1b8026efeb9e014ddeef3d0d9abcebbb1d347937780168bfb668f9163e61f818347937631e6872bdff1168f2cce05cd0e4bc41f257f38b9a9fcf9f6f2967030bff479672ceb32c8bdb5b96290be2ddde98575970fefbd785c0318f84a735194f1119cf71329e13643ccdc878aac878f690f16c24e35945c6339c8ca71d194f63329e2bc8786e22e3594bc673868c672919cf5b643c9790f15c4bc6b3978c671f19cfab643cabc9788692f13424e3b98c8ce706329e57c8789693f1bc49c6b39d8ce762329ed3643c5793f1ec27e33940c6b3858ce716329e56643c9f92f1bc44c633848c6711194f5b329e06643c9792f1b424e3694ec6b3818c6725194f13329e2bc9780e92f11c22e32926e3d946c6f30919cfcb643c83c8789690f15490f11492f15c47c6731b19cf26329edbc9785e20e36944c6733919cf61329e23643c3792f1ec20e3d949c6b38e8c672019cf32329e72329e0419cf35643c7790f1dc4ac6b3998ce745329e02329ea6643c47c9788e91f15c4fc6b38b8c673719cf7a329efe643c2bc8784690f15c44c6d3868ce72a329e9bc978b692f1ac21e3594cc6936fe139ed8847de9d9475cbfce9af78de478dbc8f5e20791f36f23e7c81e47dd0c8fbe00592f77e23effd1748de7b8dbcf75e2079571979575d2079ef32f2de7581e4bdc3c87bc7059237f3798bfa3e865c639fd0bf09f83f8edbbddd11e3698351e6b703a368f85da4fe8e78a2ce79fa13e4adbc18a8e372af2f01ff1f088caeea547f8351e66d750ac7a51ee88827ea5c6d2041deca0b79c64bfaca13f07f1cf7cb559d1a6830cabcad4e1d069e418e78a2ce310711e4adbc907724e459d304fc1fc7057155a706198c326fab5338aed510473c51e7c64308f2565ec83bcff2ee5802fe8fdfe97655a786188c326fab5338cec450473c51e7f44309f2565ec83771e4db0209f83f7ee7d4559d1a6a30cabcad4eed059ee18e78aa0c9e2a8b175f56deca0be963d8a37f13f0ff11c0e8aa4e0d371865de56a7aa806784239ea86ba81104792b2fca755cfa1613f0ff72607455a746188c326fab53bb80a7dc114fd4b55f3941deca0b79c65d9ecd4bc0ff71dc5c5775aadc6094795b9dc271e82b1cf1445db356d443de51d75ff59177d4b5447de41d755e5c1f79479de3d547de51e72bf59177959177553de61d751ca98fbca3da44bf7ffbfd3beebcbfcc6349959177553de6fd65eedf5f669bfa65b62dfebcc5b76bf595b73f6fe13e6ff92cbebc93787dda24c6f5e27d816dfa17efed6c054deeb56c014dee976d064dee79be0a9adcb7de049af43d6c044dfa8fde024dfa00b16f52bef97b1c3479260dfbc4648c89a3a0c95814d817f32b1d3f0cda1c1dc73e80d93a7e10b45fea38de7b9ea5e3fb41fb858eef036da68eef05ed431ddf03dacf75bc0ab44a1dc77b3c33747c17681fe838de5b98aee33b407b5fc7df046d9a8e7f0ada7b3a5e01dacf2cda541ddf00da141d5f0fda4f75fc15d026ebf83ad07ea2e36b419ba4e32f83f6631d5f03da8f74fc25d026eaf88ba03dabe3ab417b57c75f006d828eaf02ed873abe12b4f13abe02b41fe8f872d0beafe3cb401ba7e34b417b46c79780f68e8e2f066d8c8e2f02ed7b3afe0968a375bc1cb47c1d1f015a031d1f0e9abc17341434f9d6eb10d0e47dee41a0c937df0782d644c7fb8326cf92e37834f27d491c8f46de01c4b16ce43bd31f8126df6ec0b167647c101ca346be71f52bd09aeaf81cd0e45bb2b34193f7c57f099a7c537e1668f29d9f5f8026cf86cf044dbe17f92168f28ee1cf4193ef58578226df8698015a331dff0034f966d574d0e4bd9df741936fd54e034ddeb77e0f34f966fdcf402bd6f1a9a0ddace35340bb45c77f0adaad3a3e19b4163afe13d0e4db13934093ef2dfc1834f966d88f406ba5e313419377279f054dc6b77917b4363a3e0134f98edc0f416ba7e3e3416bafe33f00ad838e7f1fb4a48e8f03ad44c79f012da5e3ef80d651c7c78056aae3df03ad938e4bbba0f63fb55fbea6e7cb82f8cea7547e678373a73c63be0ce2c2803c719e6315010fe6752af6b2a792f8cc53be5eafd4975390f789d8f34ea6f33ea9d755a0d77bc2c8bb00d2fceddaea6d734c6b0df472a78de5f01a58d62dcbb405fdb8b1eea6babc271d95f784c124dce883a469a01b4d752c3ba8e38e9ea54aa97d40ea5a001ee25406717c5e307eaf52493c07af0dcf49e0897f3f4996b8aa13b86fc5d986d8ee019975ad08d2e09893c71df887fbbaac5be6253fcfec993db367f6cc9ed9337b66cfec993db367f6cc9ed9337b66cfec993db367f6ccfccc8a47fa13b0cf5cd2bd46c2281af6759d8c9f27897daeb27ed5afb311fa755cf7ef35d4eb6d6794b900d27c047d4e5b74bc10fe2fdb2d6a5b3ae827ac715b4a7e85509e93c0e3a2bf380ff292759fb27821f1e2d8f24e55b8f1389554cf99a83ef6d386afaf593c75b5bf62df2d7a8afbeb598307fb460b81f775fd9b80f5bc0e6570b08fd7582f243fdc974e8126f1b3c0e8623be3b144da83b67a1efba625cd69edadf48bc7bfed534997edc631285359707efd2e80346f40dbf7968ee3b321af816fef5afe2f534dfdd4e29f2af391f8cb9cc467e065fb1eb1e47d085863cafb9ce7eff374907c442f80f80fafab4e2be9c40ff15ad8f1fbd6c86e2e77d658ae08d21cb594bf2c88b7fc470c9e2306b3da266f433d7b178effaedaa4a3111eb5058f240d9e07b9fa668fd9460a87e4a7d2c8f66f64a4c1731649f301b451aa2cd2ce4b39f1d9163c06b83a8ebd66944fe66de7c62781d12ca3aa1fb32fabe6bdd09ffffa03b417713ffff507a843f89c5660acbf1dac5fb81a05d1c71649f367e338eae21a03b7a5e927fa2c69fe02edd00dfa61dc6ccef5bfacebb6a873fda30e7812c1b9d7de6aaae9f88ec798630e781c9533693b761d37ca5404695a40391d9cc7d4f81ee111c8dbc536472fe41cea84e14501a4f99f46db11e5235eab9eac97b2a4ace783ed2c6591349f19edd461074c2eb7db2128935aef294b59254d8366d5be34d4f1046c27bc6f72a5e5ff32d5d41e887faacc07e22f7312dffd94ed7bc092f73e608d29ef73de3b95f37dc947f402885fd1ac3aada4133fc46b6157fb88bcdb86ece672278ce58a20cd414bf9cb8278cb7fc0e0396030ab6dd204ea99d42397ede6c1088fda81479206ef1fcbb11ddf6fb41df75d3c5f5dd371ff14309aed269ebbb86433bf0b6bde43b59d0f4a1a3c279334b768e6a6c1b9efe3609bb3d1ed33ff25f8fe6a00e5088cb206069b239f4bd167f31e601bd04d9fe57fb66b86b6e0737ddc9f166ef35a00af4784db3ccee0f5480970ab7a2fdb09ef238af686fe75744e5e8ad75b728c31cfc9f13e5a1760c76585eb754b59c403499b0fff97ff7d16d8af4f3e6f39337ec65846f9fb8691ce964f135836b677354a924993ffb4c1896db3a4b3f92afb01d6395997b9afe07d5149d3d76893ccb46adb4f6d5aed8f6c47f10edb13ac93aefa60ce18e59779c94f31ca3da033c0e3e23e4ab6ef4fb9becfe6a88d4ee2bd8bf8beffd06d94ed9ecb6b865745c1f9f7335cb6e9517d39272c79c7e7456ab4adbfdfe6457df6f7477971dc92777c5e74aeb0b573362f8e59785cdc7fa9c98b6396bc63f462aced9e88cd8ba3161e57d7db515e1cb5e41d9f175dacf7ab6c5e1cb1f0b8baee8af242f2cb96f918017313231e4fdea5a3f0be544d5e1cb6f0c47f4faa662f0e5bf28ecf8b92ce35f57fa217872c3caefa31a3bc3864c93b3e2fba76b3ddbfb07971d0c273b09ebd3868c93bc67a31c6767fc9e6c5010b8f837b8d357a71c092778ce7879df15e634d5eecb7f0ecaf672ff65bf28ed18b72db7d509b17fb2c3caeee834679b1cf92777c5e8ceaa4f2de5b0b2ff65a78f6d6b3177b2d79c7e745795795f79e5a78b1c7c2b3a79ebdd863c93bc66ba874bda8aa851755169eaa7af6a2ca92777c5e8c4e9f6bedae8517bb2d3cbbebd98bdd96bce3f322993ea6eeaa8517bb2c3cbbead98b5d96bc63ac17e9ebc99db5f062a78567673d7bb1d392778cc79174bdd8510b2f76587876d4b3173b2c79c7e7c5d8f4fda7edb5f062bb85c7d5b88c515e6cb7e41de33d9774bdd8560b2fb65978b6d5b317db2c79c7e745c7f431756b2dbcd86ae1d95acf5e6cb5e41d9f1763d27d625b6ae1c5160bcf967af6628b25ef18cf3bd3edc5e65a78b1d9c2b3b99ebdd86cc93bc6f3cef4fd8b576be1c5ab169e57ebd98b572d79c7d876a6cf3b37d5c28b4d169e4df5ecc5264bde319e77a6bdd8580b2f365a7836d6b3171b2d79c778de993e8e6ca885171b2c3c1bead98b0d96bc63ac17e9b6737d2dbc586fe1595fcf5eacb7e41de37dad74dbf94a2dbc78c5c2f34a3d7bf18a25ef18af47d2f7f8d6d5c28b75169e75f5ecc53a4bde31f615a5cfc1d7d6c28bb5169eb5f5ecc55ac8dbd5fb3a92873c8bd5daf0a200d2fc87f12c56948fb20e7cae0ccbf272ec65c93c57b626a22c2f435924cdff369ee55be380c95159d375e62528935aef594b59258d7c085ff992afe309d826af836f9759fe2f534dcf20897faaccabe32f73baaebe009c65900fe6bd0a5863cabb04f39667d3251fd10b20deb479755a49277e88d7c2aef69117751cd9cde5d61acb15419a172de52f0be22dff6a8367b5c19c7eef01ea99d423376d5786e9c5088f5a834792069fd93beb88c77c865038243f9546b67f23238d2c5b0069aed31eca73bbf21ca49433119cffdca4a3b6ac04d965dd322ff9e1fbb2a780d12ca3aa1f83e1d9cf0e5a6f1f546b25c20deb29353455d64e8eca2a79c9ba65be1330ca9814a5f5cf98aa2d63478351f17471e0198eb321534dc78b2ec0d3d9018fa372a68f435d8d327532ca540469f0ddc6ae0eca9907f9caba65be2be4ed629ba317724cbeddf0a200d27481f6ac261f651daafe965acad2c3715964ddd22ef5a887bcbb1979a78cbc13c1b9db39086adebfba01737707cc6abd3de35f6f7affba53af4bea94e4938232f5020fe22a13e62de779928fe805107f18cef3249df821c72f61577559b625b29bcb7531962b82343d2ce52f0be22d7f4f83a7a7c1acb6491f38b773b03fa4eb400f8343e653e05dcf08ef7a807792068f7f1d1c79d7dde091f90ec023e7385d41937305e14fc0ff93f5c06db67b5d2ddca27503c60e16c6d2f819d3e73a1d0c46992f0546d1ba034f37479e99dbfa76c31f3c2e3732d2c8b20590a61c8e8d094b5ab5dffd2b944bc62e8c719c9f749bdec8815f38ae6200fe04868701f825e56ce880e7e2a07a6cc5199553a6973f33a6ff984cd7a3a0151898f89b6729463e68186f60d182e0dc21240b409321241b82966fd88243574a7a19d2ce855de887acbbc0e06c022c71e68dc36fca5453d5690c3c2eaab2aa3a97e875e9aa3368faf8ca31583f1a1a9c75a93bea7f0d6a4817b52e57dbc1dc27ca60deac83058ef26f00e52d8379c94f6d9b421d9f5a5ef16c9fe9cfcc9c346672e50c34cadcb1319e179cbb01ccdf28c35ded745801b0c0d8383434ca850d86fc4f36ccc5f17396e218b7a63701e427d3c5e0db450e7c53eb97b16a2bca274eec3773d4c4f1157d674eaea81c3f65326ecd268673515b5afedf08345b138f69d584cd162edbd8a2d9261c15b8096872e4ba0834e1b918b4061097f4e69671525d5bc0fa659752ff53e634d4056f1c545701391cab7655edbfea54eed220732a745990d99c6aa86135b4b01a4a580d1dac860a56a36caaaf5aa89e1c75f6a486f65543f9aaa17bd550bdc5416628de5b82eaa176d5f42e70de16644eb5d4d0b9ad82ccd0b8ea76a57a5d5d7dda4d7d4a439db2ab534b759aab2eebd42588baada64e3dd52d0b75fb4a9dc6a9536475faa74ed9d42588baf450978aead2a9b7f6ba4f18ee0ac3dd61b8270cf786a16f18ee0bc3fd6178200c0f86e1a1303c1c8647c2f068181e0b43bf303c1e6486921e10862782cc50d34f069961a807079921aa9f0a32c3570f0b32435b3f1d6486bd1e196486c41e156486341f1d6486ce1d1b6486da1d176486f09d106486069e1864861c9e1c6486329e1a64864856c329aba197d590cc6a486735fcb31a2a5a0d213d370cf382ccb0d30bc2b0300ccf85e1f920336cf7e22033ccb71afe7b5990192e5c0d23ae8617575d09aa0b45752da8dbf6aafb4add5a565d5aea96b7ea8a555dd3aaab5e3dbaa01ee5508fb6a8477dd4a34fea5130f5689c7a54503d3aa91e25558fd6aa478dd5a3d75541e6d1fcbd41e6d50df52a8b7ab547bdeaa45efd3a1c645e0d54af77aad725d5ebbfea75e89341e676b67ab55e7571aadbd8ea96bebad5ad6ebbaba1e5df0ac3d7c2f0f530bc1d866f84e19b61f85618be1d86ef84e1bb416658e27782cc10c83f0832c328abfaa8866cfe719019f6f9a7416688e9f782cc30d51f049921b13f0c32c36aab21b8d510debf0e32c380ab61bfd5f0e7bf093243daff360cbf0bc3df85e1f761f84318fe3e0c7f0cc39fc2f0e730fc4318fe310cff1486bf84e19fc3f02f61f86b503df4353616bfd73337ebf9f2caca3193a65616574e299e347362e5f8a913e716cf1e5f39ae78caac31d3c74e9c321b175ea39b2719b7bbcff4e9e5738bc74f1e3d664ef1949995c553c6168f9a3273f2e8730ed4c7f442d79f9f63f9e8d1d1997d23ff0b907ea78e99bea7979311d11fa8b96c3faf8b211fd565a1ff55c702cdd1472fb97d322073ae5b3c63e294cae264f1e4f06f78709d327bcce8f6c5f8bf19a1c9332a8b6754964faf2c1e3b7dcaa4e292f6b8de8f1bd7a110ffded80dcc2557d5cd9ccff4f8ce75aa62a7aeab8303ef5f5737d2df5ff70548ff54c74cff475d4af87febb2d0cdcdea46d8a659a42d33668eaa9c5e5e5119bd70f28b2cdcb9591d8a796f1d8bf9dfeb92d97fd665a16b9bd78db073f33a6436328bcc82ff07d6af973f08fc04009b2d6c6f000000212f1f8b08000000000000ffed9df77f1cd5d5c667d55cd65acbbd0202e322abad569265b9ca40006363834d31cdb8c960b02d638b964220150209bca9d4407a25bd92de437a4842494823fd87fc0bf9bce7cedef3ead1f51dbd5a31573963ce7c3ec773e7f1ddb9dffbcc99bbab3bb3b3b9a8bcfc932267cbd5148ba2e317feff3ebb2ebeb8a523c57d154372e632c2599511ceea8c70d66484b336239c7519e19c9011ce8919e19c9411cec919e1cc6784734a4638eb33c259c808e7d48c70366484735a4638a767847346463867668473564638676784734e4638e76684735e4638e7678473414638176684f3a48c709e9c11ce5332c2d99811ce5333c2795a8a9ccdc0c973e1a7dbf562bb5e62d74bed7a995d37d9f572dbc71abb6df6d942d14ad1e6fc5fbb61a63093e225e7ff3a29ba28ba2956d8ff6bb4ffd743b192a2976215c56a8a35146b29d651acb79e6ca03883e24c8ab3285e467136c53914e7526ca4388f6213c5668af329b6506ca5b880e2428a6d14db292ea2b898e21287e5528a1d1497515c4e7105c595145751eca4b89a6217c56e8a3d147b29f651f453eca7b886e25a8a0314d7515c4f7190e210c5618a018a231437501ca5384631487123c54d8e673753dc42712bc5cb1dce5750bc92e25514b751bc9ae2768a3b285e43f15a8ad751bc9ee20d146fa4b893e22e8a3751dc4d710fc59b29de42712fc57d14ff43f1568ab751bc9de21d14efa47817c5fd140f503c6859f8447888e2618a4728de4df128c56314efa1782fc5fb28de4ff1018a0f527c88e2c3141fa1f828c5c7283e4ef138c527283e49f1298a4f537c86e2b3149fa3f83cc51728be48f1258a2f533c41f1158aaf527c8de2eb14dfa0f826c5b728be4df11d8aef527c8fe2fb143fa0f821c593143f723cff31c54f287e4af133abfddcae7f61d7bfb4eba7ecfa5776fd6bbbfe8d5d3f6dd7cfd8f5b376fd9c5dffd6ae7f67d7cfdbf5efedfa0f76fd47bbfe935dffd9ae5fb0ebbfd8f55fedfa6f76fd77bbfe875d9beb6333a695cb13a3a1a52f4a69cce9eaef35f3e36ca67bfdcd5cdba9b6ffc7eb46abd7d86d5ee7ac5e6bb76b1dbdce6ed739fb9968b7273a7ac16e171cbdc16e3738fa74bb3dddd167daed998e7eaadd3e15f4bcfdbf72c7ca2ba3555b29071ae75f1568b556ab06ad8e7707da04abd582c6c7b70eb449569b00da64ab4d042d6fb549a04db1da64d0eaad9607ad60b529a04db55a3d680d562b806653339a0ada74ab358036c36ad3409b69b5e9a0cdb2da0cd0665b6d266873ac360bb4b9569b0dda3cabcd016dbed5e682b6c06af3405b68b5f9a09d64b505a09d6cb585a09d62b593406bb4dac9a09d6ab553403bcd6a8da0f179792a68a75bed34ab999ce2712e7e8dd5ab403b9dcf65d016f3790cda123e87415bcae72f68cba06dd69af87c066db9d5389fcdffadb0e5be28adb1acb4c7ecb727edfdd29ecd7e7bd3df6fd18c8daba221affba09d1ef06ab52da7784f4007b69db3c1edb05e03e5b3a12ed7633f784c627633ceacb4e5d523bc6e85f3ba02d459e9e97f5f946eff7b1d9e5e87b916ca817276bfe6eca8978a73f622a8ebe61ebf3f9e8839bb1138d2cfd9ce4ecdd9512f15e7ec1ea8ebe61e7f563b1173f652e00890b33d6172b654d49c2dcf8f44913ff7f8ef86133167fb8123fd9cedd69c1dfd5271cede0675dddce3bf614fc49c3d061ce9e76c4f8f7e3618f55271cede0375dddce3f994133167ef008e0039bb47c7d9512f15e7ec8350d7cd3d9edb3b1173f65ee0483f677b03e56ca7e66c54bed61545fedce379e61331671f018ef47376afcecf8e7ea93867bf0075dddce36b1e2762ce7ecc96cd75869fdbeb0c0b41fb85d54e02ed97563b19b4a7ac760a68bf826b83acfd9aaf1b82f61bab9d06dad3ce3555a33d63b5d3417bd66a8b417bce6a4b40fbadd59682f63bab2d03ed79ab3581f67bab2d07ed0f566b06ed8f566b01ed4f566b05edcf566b03ed05abb583f617ab1541fbabd53a40fb9bd54aa0fddd6a9da0fdc36a5da0fdd36add5633d724f9dad7f7ad3611fade17a597a3f1f5ae68f89273b6fba0bc3c2c4fb1100dbf0f88db6a49bfad4ed3f7e668f47d6f019ed6007dcf431ba3e169059eb6f479e2efc9b5a7bfdff818373b9ee6a1ad66e8573140bf72d016ef9bb7b9bd0268386e143d8c1de93396f05e27de376f7700236b388ef138cce78f798ff917f0063897e2cf19dc1e7fff97395a40e73adf6a1862fbb7d5eae1ff714c6f73b4407919e705b7c5fbe6ed7660e4feb48d3f6369b48cad0e63a83122076df1beddb6f17c5f3efe9e8deab81640fb2f8c49a5b18e49f5c0d6048c4d817c4c3ad652da0ef17e958336786c63cfdb41e73acf5595d7666c9b9c1b620b70fe952afdfc86e341fa795c2ae2793d1a9e22f08438f7039daf457cdfff4f946eae951cafda1caf0a50a703fc2b05f06fa4cf21dc9e322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb332cb67c6fb2ff0fa26d76b12c2c85a3bf08498e78fbf4bcefbb26b735de721b8ae93fe758b5211af59f27d7b4b9c3ed7409dc77343751fb565dfb541bca6d914d6bb51dd67511f1d7f2d36e435c4a4ebc0beeb978da9b55dda1bea7a9b79ce85792641b3e36b93c7d300f7a90cf334e7788af7c72d73784c9e3e5f35c416e2da5fa5d722d12b2ea7796d0fef31087b5ccae307e74255347cfcc0f799ced4db1e7e0d93af97773a6dd7409dafe4868e0ddf03c9bf75e0def364ea7439fbe6d72c01bdcbd977837d2d73d43afb6f86d7729dafc398ba1b7235c4fd1f382e47d0575cfaa08cd7cdd37f1f2e5fc72f56c053029e10e34ca0cf1b453c07d2be8edfe578e5fb1cc3753ac1bfae00fef93e8bf236b7a7cccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaaccccaacccf299f1bba2cc9a877aed4218c7e9de87f87a063f7784f76faeeb6cad1a6a37f47538bee6b4d4e9730dd4b9af6aa8ee765bae8f8ebfdf21e95806b89e37e2b1e4f6eaa13f782d28d4f7b93b1c9e0e8f175c6e4cadedf275fcf43d1eba8e5f747c6df7781aea7cc56bace8299eaf6d0e0f5e1bad8f8ebfb7240ffb198f7b8792f282dbc373a903342ee3f7a3431c677c2f71efebe1f6f0faf5f5d6db8628d4b12f15438e1bddd0a7bee8f8fcae813a4760ec3b6acb780f07de3b7287e7ff7919e93a35fb17e8396ef1f15d099c7dd00eb6dd0bac29b5dd816de76c703bacd740f9f6aaa1baee73cad86b6637e788fb2c35dfebda9cd715a0ce0a4ffffba274fbdfe3f0f438cce698dc08797607bcff871a93562478b4043ce23af83928d43d79ee18e9dedf88f7edd53975f0330bd7b90bc6a8a4fb477df71c867a1f4bbae7d0f7d9b8048c6e1fddfb3c5feaf7693d00e345daf7693d003984f76945cefe97c2fe99ab2e4a7e6fe13a8f38fb773f93f36bf03e30aef3188c174fc1671df7f337de33351e7f5f25dd27cdede1e71a3cb7ffbfbe1be66ea8df972233e604b2e0e704aef351e798752570b7785efb78c26bd92b7e0e15fefde2fa677c48fd19b2f69cef71fac279bd02fac2753eeb7c060cf59ceff4fb3afc33098f031d9ebe729d2fc1b9f6047cc6e3e3847f773ce9f97f5e46fa0c88cfbd5d957e9fe3e38bcf77ed8376b0ed35c09a52db1dd8367f06e47658af81f20f614ce77aec077bcdece61ce1cf51c8eebeaed3795d01eaf47afadf17a5dbff550ecf2a87d91c93af419e3d099f01438dd5bd091e2d058fb80e7e3788df47f019b2bef79850f7f627bdc7f89e4189e3f35341e7d5fc6ceebc9aef3302d7e1d7e2678467609ccd7beabaf385fc7e99e67dc3f85d896668f7794fbb7d29fbd90abef5c1367e2ef86fb61de0b9a5f17744f0b982cc804b1f9499a100afabca00637506186b32c0589b01c6ba0c304ec800e3c40c304eca00e3fffddeb760c67c0618a76480b13e038c850c304ecd0063430618a76580717a06186764807166061867658071760618e76480716e0618e76580717e0618176480716106184fca00e3c919603c25038c8d19603c35038ca76580719132a6c2b82c2c6369ac8c8627d4ef1356f29b78e3f1fb842d9eb6025c172955da77bc5612e2f96995fe5e223314a2e37fff2b106369ac8c86a72d7d9e8a7f5fb30d3cf3fd0e6600c617f51b52929edb86f7a72f0fcb587a31cf960b715fe7589f1f86f7182c0fcbf8a29e7116e21e824a9f71e6bbe7617958c6d2581943ddcf8cdfe7180d0f7e3faec3e35900c6d25819437d0f02bfab351a9e4ef0ace4f12c006369ac8ca1ee99aaf49e3ebcb7bbd3e3992446e449fbf9a29d9eb6ba05f49d19b2c63829038c9333c098cf00e3940c30d66780b19001c6a919606cc800e3b40c304ecf00e38c0c30cecc00e3ac0c30cece00e39c0c30cecd00e3bc0c30cecf00e3820c302ecc00a3fe5da88cff6d46e4694c8f67d8fc07b615e27bad95f67d858727c5ef7d0eeb3bb615e0192715f71d9f059225c6ee0c30067e9e8dfaa88ccaa88c7a5eab8fe219d547f5511995511995f1a5ce9885315c1933918fa5b1321a9e95813ceba980672578c6af5b9e01c6151960ec0ecb581a2ba3e109f18ce13cb4311a9e5ef08c5fb73c2c6369ac8c8627c0f31063cf7a2be0c1e706f67a3c0bc0581a2ba3e1591dc8b35515f0ac06cf56793c0bc0581a2ba3e109f1ecc73cb4311a9e35e0d96a8f6701184b6365343c6bd3e7893d5b5301cf5af06c8dc7b3008ca5b1321a9e75e9f3c49eadad80671d78b6d6e35900c6d258190dcffaf47962cfd655c0b31e3c5be7f12c006369ac8c13414f9127f66c7d053c7de0d97a8f675219bb32c0d89d01c62cf8a88cca288951cf6bf55112a3faa83e2aa3322a637619576480518fb5324a65dc903e63a952c60dc0d3973e4f47a07ec6bfe37586dd578acf962819afce74bc5aef7855803a67807f6706f02f07edf2be799bdbab94f97401cc81da2ef1ef4baf77daeb76fc30cb59765d0b7a1778fa325be6dff0e3faf87b6e5ce712fb8329fcdba0bc54417fcf4abfbf1d239ddbdc1ef2ac11c6b340184faf309e59c278a60ae399248c6789309e6a613c0b85f12c15c6335b184f49184f83309e76613c9385f1340be3a911c6b34118cf22613c6b85f1ac12c63347184f8f309e69c2789609e3c90be3a915c6b35818cf5c613cd385f11485f14c11c6d3248ca74e184fab309e75c278560be399278c67a5309e19c2783a85f1d40be399208c67be309e99c2783a84f11484f1b409e399288ca745184f4e004f3e3afe9e853cfcff06d0aa9cd79af1b366dad0ff9f6df52a78cd39b65cedd9f7d9a0f1b5dd733caf459fce86bef4d972f1c52db14fd8561f6c737bf5c0718e109e16613c1385f1b409e32908e3e910c6335318cf7c613c1384f1d40be3e914c6334318cf4a613cf384f1ac16c6b34e184fab309e3a613c4dc278a608e3290ae3992e8c67ae309ec5c2786a85f1e485f12c13c6334d184f8f309e39c2785609e3592b8c6791309e0dc2786a84f1340be3992c8ca75d184f83309e92309ed9c278960ae359288ca75a18cf12613c9384f14c15c6334b184faf309e05c278d608e3a9f2f06c08c4c3d76f79dfbcbd4148db018e43fcbdf87303f569a3dd57addd2ff3737b3550e7b1baf2da7c7ec2d732977bbd1dff76d8081e6d0cd4173e1e39e7f8046ebb0bef2b88802172fc893c3c21eec708d4cf617998e2f3198ac6abf31cafdc6357803ae7827fe705f0cf97dbbccded29b3322731e3ef5b306b1eea750a6164edaca03c5dc54a7f83e33ce009f15e11c8f7781cdbe4f4a9d3e33bd7c15cdd14a09fbe7387b73779da6e8cd2f562f328bcd8ece1d93cce5e707b95329f9b4166f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466f5597d4e62569fd5e72466093e1b1e7ea606b3e6a15e8f1046d63686e589bf6fd4130d5f72ce761f943703cf7901fc09d4cff81ef2f39d3ef5787ce73a787e9d1fa09fbe7387b7cf87e35009f3a60c32abcf6363363cfc6c4466cd43bd954218593b2f2c4f3c8ead8c862f238d63e7034f88713e503fe3716c8bd3a7951edfb90e9e5f5b02f4d377eef0f616380ecaaccc3e66c3c3cf6860d63cd4eb15c2c8dae6a03ca5f8fb8dbdd1f065a4716c0bf08418e703f91e8f635b9d3ef57a7ce73a98ab5b03f4d377eef0f656380e95306fca20b3faac3e2731abcfea7312b3faac3e2731abcfea7312b3faac3e2731abcfea7312b3faac3e2731abcfea7312b3faac3e2731abcf2f1d9f0d0fff7608b3e6a1de2a218cac9d1f94a733beeeb02a1abe8c74dd612bf06c499da77cdd2180eff175870b9c3eadf2f8ce75f0fcba20403f7de70e6f5f00c7e14467de944166cd8df161d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e6296901b8667b52d336b1eeaad16c2c8da96b03cf1730f5647c39791eedbb90078b606f027503fe3fb762e74fab4dae33bd7c1f3ebc200fdf49d3bbc7d211c076556661fb3e15963cbcc9a877a6b8430b2b6352c4f3c8ead89862f238d6317024f88713e503fe3716c9bd3a7351edfb90ee6eab600fdf49d3bbcbd0d8e83322bb38fd9f0acb56566cd43bdb5421859bb202c4f3c8ead8d862f238d63db8027c4381fa89ff138b6dde9d35a8fef5c0773757b807eface1ddede0ec7419995d9c76c78d6d932b3e6a1de3a218cace1e71f66fb4f7a3cdd0560a982b6428de191d3775e305f78d9208c6781309e59c278a60ae399248c6789309e6a613c0b85f12c15c6335b184f49184f83309e76613c9385f1340be3a911c6b34818cf1c613cd384f12c13c69317c6532b8c67b1309eb9c278a60be3290ae399228ca749184f9d309e56613cf384f1cc10c6532f8c6782309ef9c278660ae3e910c65310c6d3268c67a2309e16613c39013cf9e8f8eb9c78fda41a34be9eb10eb48b6c790368559e36783fdb41e3bfcf791f663c9e3aedf8f6aa3ced6df7708d879fd8561f6c737bf5c0b15d084f8b309e89c278da84f11484f17408e399298c67be309e09c278ea85f1cc10c6334f184fab309e3a613c4dc278a608e3290ae3992e8c67ae309ec5c2786a85f1e485f12c13c6334d18cf1c613c8b84f1d408e36916c63359184fbb309e06613c25613cb385f12c15c6b350184fb5309e25c2782609e3992a8c6796309e05c2783608e3a9f2f05c9c3e4ffc9d32ee7b045cb8f441f9e2c0fe04ea67d1ecf712bbaf14bfcb127f57ed52c7ab0d8e5705a87309f8776900ff72d02eef9bb7b93d6556e62466c3c373ab78bd94ebb50a61646d5b589e787c6c8d862f238d8f97024f8071ac23503fe3716c87d3a7568fef5c07737547807eface1ddedee169bb314ad78bcb46e1c5651e9ecbc6d90b6eaf52e64b32c82cc167c3c3f72a306b1eeab5086164ede2b03cf1f8d8120d5f461a1f2f039e10ef1f81fa198f09973b7d6af1f8ce75f0fcba3c403f7de70e6f5f0ec7a112e61d1964569fc7c66c78784e9b59f350af5908236b9706e529c5bf51d91c0d5f461ac72e079e10e37c20dfe371ec0aa74fcd1edfb90e9e5f5704e8a7efdce1ed2be03828b3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb36c66c3c3df4d63d63cd46b12c2c8da654179cad71d9aa2e1cb48d71dae009e10d76502f91e5f77b8d2e95393c777ae83b97a65807eface1ddebe128e83322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb332cb66363c6db6ccac79a8d7268491b5cbc3f2c4dfdb6a8b862f235d77b81278425c9709d4cff8bac3554e9fda3cbe731dccd5ab02f4d377eef0f655701c9459997dcc86879fb5c5ac79a8d72e8491b52b82f294af9fb647c39791c6b1ab8027c4381fc8f7781cdbe9f4a9dde33bd7c15cdd19a09fbe7387b777c271a88479470699d567f53989597d569f9398d567f53989597d569f9398d567f53989597d569f9398d567f53989597d569f9398d567f53989597d7ee9f86c78f837b398350ff58a421859bb32284f677cdda1180d5f46baeeb01378425c9709e47b7cdde16aa74f458fef5c07cfafab03f4d377eef0f6d5701c4e74e61d1964d6dc181f66cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e6246609b961783ab83dbbce43bd0e218cac5d1596277eee4147347c19e9be9dab816767007f02f533be6f6797d3a70e8fef5c07cfaf5d01fae93b77787b171c875dcaaccc1e66c353b26566cd43bd921046d67686e589c7b152347c19691cdb053c21c6f940fd8cc7b1dd4e9f4a1edfb90ee6eaee00fdf49d3bbccded29b33227319bb6f7a4df763c1e60dbec4fe4f0f0b227b01781fa198f077b23bfc7dc5e01eae031df1ba09f396897f7cddb7be13854c2bc2383cceaf3d8984ddbfbd26f3b1e0fb06df627727878d917d88b40fd8cc783fec8ef31b757803a98a7fd01fa99837679dfbcdd0fc7a112e61d1964569fc7c66cdade9f7adbe5e7a461dbec4fe4f0f0b23fb01761fa591e0fae89fc1e737b05a883797a4d807ee6a05dde376f5f03c7419995599995599995599995599995599995599995599995599995599995599965339bb6af4dbdedf2fc3db6cdfe440e0f2fd706f6224c3fcbf3f70722bfc7dc5e01eae0313f10a09f396897f7cddb07e03828b3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb3322bb36c66d3f675e9b71d7f1f07db667f22878797eb027b11a89ff1fcfdf591df636eaf0075f0985f1fa09f396897f7cddbd7c371506665f6319bb60fa6de76f97a1eb6cdfe440e0f2f07037b11a69fe5f1e050e4f798db2b401d3ce68702f43307edf2be79fb101c874a98776490597d569f9398d567f53989597d569f9398d567f53989597d569f9398d567f53989597d569f9398d567f53989597d569f9398d5e7978ecfa6edc3a9b7dd19cfdf63dbec4fe4f0f07238b01761fa599ebf1f88fc1e737b05a883793a10a09f396897f7cddb03701c4e74e61d1964d6dc181f66cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e62466cd0d654e62d6dc50e6246609b961da3e927edbf1f7d9b16df6277278783912d88b40fd8cef7fb921f27bcced15a00ee6e90d01fa99837679dfbc7d031c076556661f339e2713d36b3bbe1f8edba8b26ba31db5e56ad08ed9720d6883b65c0bda8db65c07da4db63c01b49ba13facdd62cbcb40bbd5964ba0bddc96af06ed15b6bc0bb457daf26ed05e65cb7b40bbcd96f782f66a5bde07daedb6dc0fda1db6bc1fb4d7d8f235a0bdd696af05ed75b67c00b4d7dbf275a0bdc196af07ed8db67c10b43b6df9106877d9f261d0de64cb03a0dd6dcb8b40bbc7a3bdd9962f01ed2d1eed5e5b9e04da7db63c19b4ff8132afdf6acb53407b9b2dd783f6765b2e80f60e5b9e0ada3b6db901b477d9f234d0eeb7e5e9a03d60cb33407bd0966782f6902dcf02ed615b9e0dda23b63c07b477dbf25cd01eb5e579a03d66cbf3417b8f2d2f00edbdb6bc10b4f7d9321ea3f7dbf211d0781cb801341e078e82c6e3c031d0781c18048dc7811b41e371e026d0781cb81934ce9d5b40e3dcb91534ce9d9783c6b9f30ad038775e091ae7ceab40e3dcb90d34ce9d5783c6b9733b689c3b7780c6b9f31ad038775e0b1ae7ceeb40e3dc793d689c3b6f008d73e78da071eedc091ae7ce5da071eebc0934ce9dbb413bc996ef01ed645b7e3368a7d8f25b406bb4e57b413bd596ef03ed345bc67181c79fb78276ba2dbf0db4c5b6fc76d096d8f23b405b6acbef048ddf73de055a932ddf0fda725b7e00b4665b7e10b4165b7e08b4565b7e18b4365b7e04b4765b7e3768455b7e14b40e5b7e0c347e9f7c0f689db6fc5ed0ba6cf97da075db328f0be6fc33e70cf799fda887fe357bb85933e73473f745e97e66e2b678dfbccded1946f69bff6f1c194ba3656c73180d4f2980679843bc8cf4b75f09783a02f004ea67fcb75fa7d3a7a2d3a702d4391dfad919a09f396897f7cddb9dd07688638e5ed4dafd2e76bca8813acfda3739b31ac947de87c9df764f5f5604ee0bef9bc7a515e3d07697d376abd3368ebdbc8c747e750173770066b3df9ef4f71b9f5f2bedbe38a7b89d56e8532f7890569fb0ed9c0d6e87f51a28ffbb61a82ed7633ff8fd8bd94d2ef3b14476f77525e77505a8b3c2d3ffbe28ddfef7383c3d0eb339262f340c7104381fe21c58e170f0762b78d793e0dd0af08eebe0fb5f7b20efba1d9e6ea76dc3c39f713a41e3cf0a3807c1ffdf3c0edceeb8d7e9e166ad0b187d9f75dad2671cf1b34e1b30b2d60d3c5d813c738ff562c71f7c5fae73eaf06b6ba04e95fd63af211afeb706d735e7ddbfa05ffc37f37fa274c7f4ba007ee1dff311f813391e46e017f7b33600cfe468e86ffa63830347775fd3bfad7ff7be1ca0d53898b8ce79ba51051a96ab3d5a140d9fbac0294f9ebac029cf2ac7169c32e1fae64f29d32d9e1ee83f7460f0e2c3fd87f71ebdf5c860ffbecd03d72075ad438fa4493d4052d47c3d1a897e5274fcc14ff5cc64b845b0ff9c05a9b69da8b540c6be89d1907d3cb363deadcd8862666acc19694e4e63ad996931332b6626c5cc9c9899123333626642cccc8799e930331b6626c3cc5c98998ac6a83c13711af07c1f18cd5f08e66c37330966e6c0cc14987709f3e9d38cace6938479d733239d19e1cca764f317931939cca86c461c334a98773df36e673e9d9877eb5514ab29d650aca55847b1de7abd81e20c8a3329cea27819c5d914e7509c4bb191e23c8a4d149b29cea7d842b195e2028a0b29b6516ca7b888e2e2a83cf3776954bec27919c5e51457505c497115c5cea83ca3be2b2acf989b197233236e66c0cd8cb799e13633da6606dbcc589b19eaeba3f20cb499713633cc6646d9cc269ad943335b686607cd6ca099fd33b37d6676cfcce699d93b335b6766e7cc6c9c997d33b36d6676edf6a83c7b6666cbccec98990d33b35f66b6cbcc6e99d92c337b6566abee8ecab35166f6c9cc3699d925339b64668fcc6c91991d32b34166f6c7ccf698d91d339b63666fcc6c8d999d31b33166f6c5ccb698d995c7a2f2ec89992d31b3236636e403141fa4f810c587293e42f1518a8f517c9ce2718a4f507c92e253149fa6f80cc567293e47f1798a2f507c91e24b145fa67882e22b145fa5f81ac5d729be41f14d8a6f517c9be23b14dfa5f85e54cec91f50fc90e2498a1f51fc98e227143fa5f819c5cf297e41f14b8aa7287e45f16b8adf503c4df10cc5b314cf51fc96e27714cf53fc9ee20f147fa4f813c59f295ea0f80bc55f29fe46f1778a7f50fc331a9a49c581e209bbc1b37abb0707fb0f1d196c1c1c683c74e3c1c103470ededa78f381c16b1b076eea3fbaffe0c0cdf8e2ebec30c453961b8e1edd7d6be381c3fbfa6f691cb871b071607fe39e811b0fef3b862fbad3be68e1f12deedeb72fb9b1fbab5e04e9c3636cf423f6753c19bc71e4be7d7c2c867c662c2f7a7a8c1dbad8be8bacb1dbdbcbefd48dc70e0e0c36161b0fd3bfbb0fd26bfaf7b535e2ff1d23938f0d361e1bdc7d74b071ffd181438d1d6db8df47ebc6d089671ac6f0a2dcb4d1f73cfa5f16540aa2d9140300", "privateFunctions": [ { "selector": { @@ -37,8 +37,8 @@ exports[`ContractClass creates a contract class from a contract compilation arti "isInternal": false } ], - "id": "0x1738259ae9a0ec25dd29d7a269df543975ed6901652d1335bca61e844f542096", + "id": "0x1310e0230fe8fa9a6c99193217d165b85d180bb916fc60afbedbe85721600ae5", "privateFunctionsRoot": "0x2dc1f38d7be98a8e72227d6f8aec393c60db813a1819c9c86b02a00cc18f6687", - "publicBytecodeCommitment": "0x024f70e86b30a79b71987ef8cf7aa040206dbc37ca446743670fab781506248b" + "publicBytecodeCommitment": "0x2bbeaacc4ec3ee2fa51a3e2720a5772c6b079629e26e39c4a187fc6e4a56e46a" }" `; diff --git a/yarn-project/circuits.js/src/structs/content_commitment.ts b/yarn-project/circuits.js/src/structs/content_commitment.ts index 8b047bf2365..e95bd6f9189 100644 --- a/yarn-project/circuits.js/src/structs/content_commitment.ts +++ b/yarn-project/circuits.js/src/structs/content_commitment.ts @@ -1,11 +1,5 @@ import { Fr } from '@aztec/foundation/fields'; -import { - BufferReader, - FieldReader, - fromTruncField, - serializeToBuffer, - toTruncField, -} from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { CONTENT_COMMITMENT_LENGTH } from '../constants.gen.js'; @@ -16,12 +10,21 @@ export class ContentCommitment { if (txsEffectsHash.length !== NUM_BYTES_PER_SHA256) { throw new Error(`txsEffectsHash buffer must be ${NUM_BYTES_PER_SHA256} bytes`); } + if (txsEffectsHash[0] !== 0) { + throw new Error(`txsEffectsHash buffer should be truncated and left padded`); + } if (inHash.length !== NUM_BYTES_PER_SHA256) { throw new Error(`inHash buffer must be ${NUM_BYTES_PER_SHA256} bytes`); } + if (inHash[0] !== 0) { + throw new Error(`inHash buffer should be truncated and left padded`); + } if (outHash.length !== NUM_BYTES_PER_SHA256) { throw new Error(`outHash buffer must be ${NUM_BYTES_PER_SHA256} bytes`); } + if (outHash[0] !== 0) { + throw new Error(`outHash buffer should be truncated and left padded`); + } } toBuffer() { @@ -31,9 +34,9 @@ export class ContentCommitment { toFields(): Fr[] { const serialized = [ this.txTreeHeight, - ...toTruncField(this.txsEffectsHash), - ...toTruncField(this.inHash), - ...toTruncField(this.outHash), + Fr.fromBuffer(this.txsEffectsHash), + Fr.fromBuffer(this.inHash), + Fr.fromBuffer(this.outHash), ]; if (serialized.length !== CONTENT_COMMITMENT_LENGTH) { throw new Error(`Expected content commitment to have 4 fields, but it has ${serialized.length} fields`); @@ -56,9 +59,9 @@ export class ContentCommitment { const reader = FieldReader.asReader(fields); return new ContentCommitment( reader.readField(), - fromTruncField(reader.readField()), - fromTruncField(reader.readField()), - fromTruncField(reader.readField()), + reader.readField().toBuffer(), + reader.readField().toBuffer(), + reader.readField().toBuffer(), ); } diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts index 438ea71f76f..326e410a257 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts @@ -22,7 +22,6 @@ import { MAX_REVERTIBLE_NULLIFIERS_PER_TX, MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - NUM_FIELDS_PER_SHA256, } from '../../constants.gen.js'; import { CallRequest } from '../call_request.js'; import { PublicDataUpdateRequest } from '../public_data_update_request.js'; @@ -62,14 +61,14 @@ export class CombinedAccumulatedData { public newL2ToL1Msgs: Tuple, /** * Accumulated encrypted logs hash from all the previous kernel iterations. - * Note: Represented as a tuple of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public encryptedLogsHash: Tuple, + public encryptedLogsHash: Fr, /** * Accumulated unencrypted logs hash from all the previous kernel iterations. - * Note: Represented as a tuple of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public unencryptedLogsHash: Tuple, + public unencryptedLogsHash: Fr, /** * Total accumulated length of the encrypted log preimages emitted in all the previous kernel iterations */ @@ -118,8 +117,8 @@ export class CombinedAccumulatedData { reader.readArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, CallRequest), reader.readArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), + Fr.fromBuffer(reader), + Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), reader.readArray(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataUpdateRequest), @@ -143,8 +142,8 @@ export class CombinedAccumulatedData { makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr.zero), - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), + Fr.zero(), + Fr.zero(), Fr.zero(), Fr.zero(), makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataUpdateRequest.empty), @@ -244,14 +243,14 @@ export class PublicAccumulatedRevertibleData { public newL2ToL1Msgs: Tuple, /** * Accumulated encrypted logs hash from all the previous kernel iterations. - * Note: Represented as a tuple of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public encryptedLogsHash: Tuple, + public encryptedLogsHash: Fr, /** * Accumulated unencrypted logs hash from all the previous kernel iterations. - * Note: Represented as a tuple of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public unencryptedLogsHash: Tuple, + public unencryptedLogsHash: Fr, /** * Total accumulated length of the encrypted log preimages emitted in all the previous kernel iterations */ @@ -295,8 +294,8 @@ export class PublicAccumulatedRevertibleData { this.privateCallStack.every(x => x.isEmpty()) && this.publicCallStack.every(x => x.isEmpty()) && this.newL2ToL1Msgs.every(x => x.isZero()) && - this.encryptedLogsHash.every(x => x.isZero()) && - this.unencryptedLogsHash.every(x => x.isZero()) && + this.encryptedLogsHash.isZero() && + this.unencryptedLogsHash.isZero() && this.encryptedLogPreimagesLength.isZero() && this.unencryptedLogPreimagesLength.isZero() && this.publicDataUpdateRequests.every(x => x.isEmpty()) @@ -311,8 +310,8 @@ export class PublicAccumulatedRevertibleData { privateCallStack: [${this.privateCallStack.map(h => h.toString()).join(', ')}], publicCallStack: [${this.publicCallStack.map(h => h.toString()).join(', ')}], newL2ToL1Msgs: [${this.newL2ToL1Msgs.map(h => h.toString()).join(', ')}], - encryptedLogsHash: [${this.encryptedLogsHash.map(h => h.toString()).join(', ')}], - unencryptedLogsHash: [${this.unencryptedLogsHash.map(h => h.toString()).join(', ')}], + encryptedLogsHash: ${this.encryptedLogsHash}, + unencryptedLogsHash: ${this.unencryptedLogsHash}, encryptedLogPreimagesLength: ${this.encryptedLogPreimagesLength} unencryptedLogPreimagesLength: ${this.unencryptedLogPreimagesLength} publicDataUpdateRequests: [${this.publicDataUpdateRequests.map(h => h.toString()).join(', ')}], @@ -332,8 +331,8 @@ export class PublicAccumulatedRevertibleData { reader.readArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, CallRequest), reader.readArray(MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), + Fr.fromBuffer(reader), + Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), reader.readArray(MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataUpdateRequest), @@ -371,8 +370,8 @@ export class PublicAccumulatedRevertibleData { makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), makeTuple(MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr.zero), - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), + Fr.zero(), + Fr.zero(), Fr.zero(), Fr.zero(), makeTuple(MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, PublicDataUpdateRequest.empty), @@ -409,14 +408,14 @@ export class PrivateAccumulatedRevertibleData { public newL2ToL1Msgs: Tuple, /** * Accumulated encrypted logs hash from all the previous kernel iterations. - * Note: Represented as a tuple of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public encryptedLogsHash: Tuple, + public encryptedLogsHash: Fr, /** * Accumulated unencrypted logs hash from all the previous kernel iterations. - * Note: Represented as a tuple of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public unencryptedLogsHash: Tuple, + public unencryptedLogsHash: Fr, /** * Total accumulated length of the encrypted log preimages emitted in all the previous kernel iterations */ @@ -458,8 +457,8 @@ export class PrivateAccumulatedRevertibleData { reader.readArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, CallRequest), reader.readArray(MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest), reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), + Fr.fromBuffer(reader), + Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), ); @@ -481,8 +480,8 @@ export class PrivateAccumulatedRevertibleData { makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), makeTuple(MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_TX, Fr.zero), - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), + Fr.zero(), + Fr.zero(), Fr.zero(), Fr.zero(), ); diff --git a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts index fea7a64366f..53e58221257 100644 --- a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts @@ -15,7 +15,6 @@ import { MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, - NUM_FIELDS_PER_SHA256, PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH, RETURN_VALUES_LENGTH, } from '../constants.gen.js'; @@ -98,14 +97,14 @@ export class PrivateCircuitPublicInputs { public endSideEffectCounter: Fr, /** * Hash of the encrypted logs emitted in this function call. - * Note: Represented as an array of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public encryptedLogsHash: Tuple, + public encryptedLogsHash: Fr, /** * Hash of the unencrypted logs emitted in this function call. - * Note: Represented as an array of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public unencryptedLogsHash: Tuple, + public unencryptedLogsHash: Fr, /** * Length of the encrypted log preimages emitted in this function call. * Note: Here so that the gas cost of this request can be measured by circuits, without actually needing to feed @@ -166,8 +165,8 @@ export class PrivateCircuitPublicInputs { reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message), reader.readObject(Fr), reader.readObject(Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), + reader.readObject(Fr), + reader.readObject(Fr), reader.readObject(Fr), reader.readObject(Fr), reader.readObject(Header), @@ -194,8 +193,8 @@ export class PrivateCircuitPublicInputs { reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message), reader.readField(), reader.readField(), - reader.readFieldArray(NUM_FIELDS_PER_SHA256), - reader.readFieldArray(NUM_FIELDS_PER_SHA256), + reader.readField(), + reader.readField(), reader.readField(), reader.readField(), reader.readObject(Header), @@ -225,8 +224,8 @@ export class PrivateCircuitPublicInputs { makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message.empty), Fr.ZERO, Fr.ZERO, - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), + Fr.ZERO, + Fr.ZERO, Fr.ZERO, Fr.ZERO, Header.empty(), @@ -254,8 +253,8 @@ export class PrivateCircuitPublicInputs { isZeroArray(this.privateCallStackHashes) && isZeroArray(this.publicCallStackHashes) && isEmptyArray(this.newL2ToL1Msgs) && - isZeroArray(this.encryptedLogsHash) && - isZeroArray(this.unencryptedLogsHash) && + this.encryptedLogsHash.isZero() && + this.unencryptedLogsHash.isZero() && this.encryptedLogPreimagesLength.isZero() && this.unencryptedLogPreimagesLength.isZero() && this.historicalHeader.isEmpty() && diff --git a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts index df9f69807f2..f9b3488558e 100644 --- a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts @@ -16,7 +16,6 @@ import { MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, - NUM_FIELDS_PER_SHA256, PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH, RETURN_VALUES_LENGTH, } from '../constants.gen.js'; @@ -94,9 +93,9 @@ export class PublicCircuitPublicInputs { public endSideEffectCounter: Fr, /** * Hash of the unencrypted logs emitted in this function call. - * Note: Represented as an array of 2 fields in order to fit in all of the 256 bits of sha256 hash. + * Note: Truncated to 31 bytes to fit in Fr. */ - public unencryptedLogsHash: Tuple, + public unencryptedLogsHash: Fr, /** * Length of the unencrypted log preimages emitted in this function call. */ @@ -145,7 +144,7 @@ export class PublicCircuitPublicInputs { makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message.empty), Fr.ZERO, Fr.ZERO, - makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), + Fr.ZERO, Fr.ZERO, Header.empty(), AztecAddress.ZERO, @@ -172,7 +171,7 @@ export class PublicCircuitPublicInputs { isArrayEmpty(this.newL2ToL1Msgs, item => item.isEmpty()) && this.startSideEffectCounter.isZero() && this.endSideEffectCounter.isZero() && - isFrArrayEmpty(this.unencryptedLogsHash) && + this.unencryptedLogsHash.isZero() && this.unencryptedLogPreimagesLength.isZero() && this.historicalHeader.isEmpty() && this.proverAddress.isZero() && @@ -247,7 +246,7 @@ export class PublicCircuitPublicInputs { reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message), reader.readObject(Fr), reader.readObject(Fr), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr), + reader.readObject(Fr), reader.readObject(Fr), reader.readObject(Header), reader.readObject(AztecAddress), @@ -272,7 +271,7 @@ export class PublicCircuitPublicInputs { reader.readArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, L2ToL1Message), reader.readField(), reader.readField(), - reader.readFieldArray(NUM_FIELDS_PER_SHA256), + reader.readField(), reader.readField(), Header.fromFields(reader), AztecAddress.fromFields(reader), diff --git a/yarn-project/circuits.js/src/structs/rollup/base_or_merge_rollup_public_inputs.ts b/yarn-project/circuits.js/src/structs/rollup/base_or_merge_rollup_public_inputs.ts index d44621ca084..87658904f2e 100644 --- a/yarn-project/circuits.js/src/structs/rollup/base_or_merge_rollup_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/rollup/base_or_merge_rollup_public_inputs.ts @@ -1,7 +1,6 @@ import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { NUM_FIELDS_PER_SHA256 } from '../../constants.gen.js'; import { AggregationObject } from '../aggregation_object.js'; import { PartialStateReference } from '../partial_state_reference.js'; import { RollupTypes } from '../shared.js'; @@ -39,15 +38,15 @@ export class BaseOrMergeRollupPublicInputs { */ public end: PartialStateReference, /** - * SHA256 hashes of transactions effects. Used to make public inputs constant-sized (to then be unpacked on-chain). - * Note: Length 2 for high and low. + * SHA256 hash of transactions effects. Used to make public inputs constant-sized (to then be unpacked on-chain). + * Note: Truncated to 31 bytes to fit in Fr. */ - public txsEffectsHash: Tuple, + public txsEffectsHash: Fr, /** - * SHA256 hashes of outhash. Used to make public inputs constant-sized (to then be unpacked on-chain). - * Note: Length 2 for high and low. + * SHA256 hash of outhash. Used to make public inputs constant-sized (to then be unpacked on-chain). + * Note: Truncated to 31 bytes to fit in Fr. */ - public outHash: Tuple, + public outHash: Fr, ) {} /** @@ -65,8 +64,9 @@ export class BaseOrMergeRollupPublicInputs { reader.readObject(ConstantRollupData), reader.readObject(PartialStateReference), reader.readObject(PartialStateReference), - reader.readArray(NUM_FIELDS_PER_SHA256, Fr) as [Fr], - reader.readArray(NUM_FIELDS_PER_SHA256, Fr) as [Fr], + //TODO check + Fr.fromBuffer(reader), + Fr.fromBuffer(reader), ); } diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 34526030b9c..bfee81cc51a 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -72,7 +72,6 @@ import { NULLIFIER_TREE_HEIGHT, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUM_BASE_PARITY_PER_ROOT_PARITY, - NUM_FIELDS_PER_SHA256, NUM_MSGS_PER_BASE_PARITY, NoteHashReadRequestMembershipWitness, NullifierKeyValidationRequest, @@ -296,8 +295,8 @@ export function makeCombinedAccumulatedData(seed = 1, full = false): CombinedAcc tupleGenerator(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x400), tupleGenerator(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x500), tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_TX, fr, seed + 0x600), - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x700), // encrypted logs hash - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x800), // unencrypted logs hash + fr(seed + 0x700), // encrypted logs hash + fr(seed + 0x800), // unencrypted logs hash fr(seed + 0x900), // encrypted_log_preimages_length fr(seed + 0xa00), // unencrypted_log_preimages_length tupleGenerator(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, makePublicDataUpdateRequest, seed + 0xd00), @@ -318,8 +317,8 @@ export function makeCombinedAccumulatedRevertibleData(seed = 1, full = false): P tupleGenerator(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x400), tupleGenerator(MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x500), tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_TX, fr, seed + 0x600), - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x700), // encrypted logs hash - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x800), // unencrypted logs hash + fr(seed + 0x700), // encrypted logs hash + fr(seed + 0x800), // unencrypted logs hash fr(seed + 0x900), // encrypted_log_preimages_length fr(seed + 0xa00), // unencrypted_log_preimages_length tupleGenerator(MAX_REVERTIBLE_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, makePublicDataUpdateRequest, seed + 0xd00), @@ -340,8 +339,8 @@ export function makeFinalAccumulatedData(seed = 1, full = false): PrivateAccumul tupleGenerator(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x400), tupleGenerator(MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x500), tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_TX, fr, seed + 0x600), - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x700), // encrypted logs hash - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x800), // unencrypted logs hash + fr(seed + 0x700), // encrypted logs hash + fr(seed + 0x800), // unencrypted logs hash fr(seed + 0x900), // encrypted_log_preimages_length fr(seed + 0xa00), // unencrypted_log_preimages_length ); @@ -434,7 +433,7 @@ export function makePublicCircuitPublicInputs( tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, makeL2ToL1Message, seed + 0x900), fr(seed + 0xa00), fr(seed + 0xa01), - tupleGenerator(NUM_FIELDS_PER_SHA256, fr, seed + 0x901), + fr(seed + 0x901), fr(seed + 0x902), makeHeader(seed + 0xa00, undefined), makeAztecAddress(seed + 0xb01), @@ -884,8 +883,8 @@ export function makePrivateCircuitPublicInputs(seed = 0): PrivateCircuitPublicIn newL2ToL1Msgs: makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, makeL2ToL1Message, seed + 0x800), startSideEffectCounter: fr(seed + 0x849), endSideEffectCounter: fr(seed + 0x850), - encryptedLogsHash: makeTuple(NUM_FIELDS_PER_SHA256, fr, seed + 0x900), - unencryptedLogsHash: makeTuple(NUM_FIELDS_PER_SHA256, fr, seed + 0xa00), + encryptedLogsHash: fr(seed + 0x900), + unencryptedLogsHash: fr(seed + 0xa00), encryptedLogPreimagesLength: fr(seed + 0xb00), unencryptedLogPreimagesLength: fr(seed + 0xc00), historicalHeader: makeHeader(seed + 0xd00, undefined), @@ -1005,8 +1004,8 @@ export function makeBaseOrMergeRollupPublicInputs( makeConstantBaseRollupData(seed + 0x200, globalVariables), makePartialStateReference(seed + 0x300), makePartialStateReference(seed + 0x400), - [fr(seed + 0x901)], - [fr(seed + 0x902)], + fr(seed + 0x901), + fr(seed + 0x902), ); } diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts index c87579abff7..0ceaeffb7d5 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts @@ -10,8 +10,8 @@ import { L2Actor, computeAuthWitMessageHash, } from '@aztec/aztec.js'; -import { sha256 } from '@aztec/foundation/crypto'; -import { serializeToBuffer, toTruncField } from '@aztec/foundation/serialize'; +import { sha256ToField } from '@aztec/foundation/crypto'; +import { serializeToBuffer } from '@aztec/foundation/serialize'; import { TokenBridgeContract, TokenContract } from '@aztec/noir-contracts.js'; import { toFunctionSelector } from 'viem/utils'; @@ -157,14 +157,12 @@ describe('e2e_cross_chain_messaging', () => { await crossChainTestHarness.makeMessageConsumable(msgHash); // 3. Consume L1 -> L2 message and mint private tokens on L2 - const content = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('mint_private(bytes32,uint256)').substring(2), 'hex'), - serializeToBuffer(...[secretHashForL2MessageConsumption, new Fr(bridgeAmount)]), - ]), - ), - )[0]; + const content = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('mint_private(bytes32,uint256)').substring(2), 'hex'), + serializeToBuffer(...[secretHashForL2MessageConsumption, new Fr(bridgeAmount)]), + ]), + ); const wrongMessage = new L1ToL2Message( new L1Actor(crossChainTestHarness.tokenPortalAddress, crossChainTestHarness.publicClient.chain.id), new L2Actor(l2Bridge.address, 1), @@ -237,14 +235,12 @@ describe('e2e_cross_chain_messaging', () => { // Wait for the message to be available for consumption await crossChainTestHarness.makeMessageConsumable(msgHash); - const content = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('mint_public(bytes32,uint256)').substring(2), 'hex'), - serializeToBuffer(...[ownerAddress, new Fr(bridgeAmount)]), - ]), - ), - )[0]; + const content = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('mint_public(bytes32,uint256)').substring(2), 'hex'), + serializeToBuffer(...[ownerAddress, new Fr(bridgeAmount)]), + ]), + ); const wrongMessage = new L1ToL2Message( new L1Actor(crossChainTestHarness.tokenPortalAddress, crossChainTestHarness.publicClient.chain.id), new L2Actor(l2Bridge.address, 1), diff --git a/yarn-project/end-to-end/src/e2e_outbox.test.ts b/yarn-project/end-to-end/src/e2e_outbox.test.ts index ca3dd3af8b3..5f7e84ca507 100644 --- a/yarn-project/end-to-end/src/e2e_outbox.test.ts +++ b/yarn-project/end-to-end/src/e2e_outbox.test.ts @@ -6,9 +6,9 @@ import { EthAddress, Fr, SiblingPath, - sha256, } from '@aztec/aztec.js'; -import { toTruncField, truncateAndPad } from '@aztec/foundation/serialize'; +import { sha256ToField } from '@aztec/foundation/crypto'; +import { truncateAndPad } from '@aztec/foundation/serialize'; import { SHA256 } from '@aztec/merkle-tree'; import { TestContract } from '@aztec/noir-contracts.js'; @@ -103,17 +103,15 @@ describe('E2E Outbox Tests', () => { } function makeL2ToL1Message(recipient: EthAddress, content: Fr = Fr.ZERO): Fr { - const leaf = toTruncField( - sha256( - Buffer.concat([ - contract.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - recipient.toBuffer32(), - new Fr(deployL1ContractsValues.publicClient.chain.id).toBuffer(), // chain id - content.toBuffer(), - ]), - ), - )[0]; + const leaf = sha256ToField( + Buffer.concat([ + contract.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + recipient.toBuffer32(), + new Fr(deployL1ContractsValues.publicClient.chain.id).toBuffer(), // chain id + content.toBuffer(), + ]), + ); return leaf; } diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts index 2dbfa75bc6d..e1d8ee5c24e 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts @@ -14,8 +14,8 @@ import { computeAuthWitMessageHash, computeMessageSecretHash, } from '@aztec/aztec.js'; -import { sha256 } from '@aztec/foundation/crypto'; -import { serializeToBuffer, toTruncField } from '@aztec/foundation/serialize'; +import { sha256ToField } from '@aztec/foundation/crypto'; +import { serializeToBuffer } from '@aztec/foundation/serialize'; import { InboxAbi, OutboxAbi } from '@aztec/l1-artifacts'; import { TestContract } from '@aztec/noir-contracts.js'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; @@ -152,14 +152,12 @@ describe('e2e_public_cross_chain_messaging', () => { await crossChainTestHarness.makeMessageConsumable(msgHash); - const content = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('mint_public(bytes32,uint256)').substring(2), 'hex'), - serializeToBuffer(...[user2Wallet.getAddress(), new Fr(bridgeAmount)]), - ]), - ), - )[0]; + const content = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('mint_public(bytes32,uint256)').substring(2), 'hex'), + serializeToBuffer(...[user2Wallet.getAddress(), new Fr(bridgeAmount)]), + ]), + ); const wrongMessage = new L1ToL2Message( new L1Actor(crossChainTestHarness.tokenPortalAddress, crossChainTestHarness.publicClient.chain.id), new L2Actor(l2Bridge.address, 1), @@ -206,14 +204,12 @@ describe('e2e_public_cross_chain_messaging', () => { await crossChainTestHarness.makeMessageConsumable(msgHash); // Wrong message hash - const content = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('mint_private(bytes32,uint256)').substring(2), 'hex'), - serializeToBuffer(...[secretHash, new Fr(bridgeAmount)]), - ]), - ), - )[0]; + const content = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('mint_private(bytes32,uint256)').substring(2), 'hex'), + serializeToBuffer(...[secretHash, new Fr(bridgeAmount)]), + ]), + ); const wrongMessage = new L1ToL2Message( new L1Actor(crossChainTestHarness.tokenPortalAddress, crossChainTestHarness.publicClient.chain.id), new L2Actor(l2Bridge.address, 1), @@ -260,17 +256,15 @@ describe('e2e_public_cross_chain_messaging', () => { content: content.toString() as Hex, }; - const leaf = toTruncField( - sha256( - Buffer.concat([ - testContract.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - recipient.toBuffer32(), - new Fr(crossChainTestHarness.publicClient.chain.id).toBuffer(), // chain id - content.toBuffer(), - ]), - ), - )[0]; + const leaf = sha256ToField( + Buffer.concat([ + testContract.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + recipient.toBuffer32(), + new Fr(crossChainTestHarness.publicClient.chain.id).toBuffer(), // chain id + content.toBuffer(), + ]), + ); const [l2MessageIndex, siblingPath] = await aztecNode.getL2ToL1MessageMembershipWitness( l2TxReceipt.blockNumber!, diff --git a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts index 7dea048cdcb..3361d04a52f 100644 --- a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts @@ -24,7 +24,6 @@ import { import { fr, makeNewSideEffect, makeNewSideEffectLinkedToNoteHash, makeProof } from '@aztec/circuits.js/testing'; import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; import { makeTuple, range } from '@aztec/foundation/array'; -import { toTruncField } from '@aztec/foundation/serialize'; import { openTmpStore } from '@aztec/kv-store/utils'; import { AvailabilityOracleAbi, InboxAbi, OutboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { SHA256Trunc, StandardTree } from '@aztec/merkle-tree'; @@ -181,8 +180,8 @@ describe('L1Publisher integration', () => { processedTx.data.end.newNullifiers[processedTx.data.end.newNullifiers.length - 1] = SideEffectLinkedToNoteHash.empty(); processedTx.data.end.newL2ToL1Msgs = makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_TX, fr, seed + 0x300); - processedTx.data.end.encryptedLogsHash = toTruncField(processedTx.encryptedLogs.hash()); - processedTx.data.end.unencryptedLogsHash = toTruncField(processedTx.unencryptedLogs.hash()); + processedTx.data.end.encryptedLogsHash = Fr.fromBuffer(processedTx.encryptedLogs.hash()); + processedTx.data.end.unencryptedLogsHash = Fr.fromBuffer(processedTx.unencryptedLogs.hash()); return processedTx; }; diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index 5d2fbc75ada..feed0f4a2ea 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -16,9 +16,8 @@ import { computeMessageSecretHash, deployL1Contract, retryUntil, - sha256, } from '@aztec/aztec.js'; -import { toTruncField } from '@aztec/foundation/serialize'; +import { sha256ToField } from '@aztec/foundation/crypto'; import { InboxAbi, OutboxAbi, @@ -364,27 +363,23 @@ export class CrossChainTestHarness { } getL2ToL1MessageLeaf(withdrawAmount: bigint, callerOnL1: EthAddress = EthAddress.ZERO): Fr { - const content = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), - this.ethAccount.toBuffer32(), - new Fr(withdrawAmount).toBuffer(), - callerOnL1.toBuffer32(), - ]), - ), - )[0]; - const leaf = toTruncField( - sha256( - Buffer.concat([ - this.l2Bridge.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - this.tokenPortalAddress.toBuffer32() ?? Buffer.alloc(32, 0), - new Fr(this.publicClient.chain.id).toBuffer(), // chain id - content.toBuffer(), - ]), - ), - )[0]; + const content = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), + this.ethAccount.toBuffer32(), + new Fr(withdrawAmount).toBuffer(), + callerOnL1.toBuffer32(), + ]), + ); + const leaf = sha256ToField( + Buffer.concat([ + this.l2Bridge.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + this.tokenPortalAddress.toBuffer32() ?? Buffer.alloc(32, 0), + new Fr(this.publicClient.chain.id).toBuffer(), // chain id + content.toBuffer(), + ]), + ); return leaf; } diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index 61c270341f1..fd1ca3c1acc 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -9,8 +9,7 @@ import { computeAuthWitMessageHash, } from '@aztec/aztec.js'; import { deployL1Contract } from '@aztec/ethereum'; -import { sha256 } from '@aztec/foundation/crypto'; -import { toTruncField } from '@aztec/foundation/serialize'; +import { sha256ToField } from '@aztec/foundation/crypto'; import { InboxAbi, UniswapPortalAbi, UniswapPortalBytecode } from '@aztec/l1-artifacts'; import { UniswapContract } from '@aztec/noir-contracts.js/Uniswap'; @@ -247,61 +246,53 @@ export const uniswapL1L2TestSuite = ( .send() .wait(); - const swapPrivateContent = toTruncField( - sha256( - Buffer.concat([ - Buffer.from( - toFunctionSelector( - 'swap_private(address,uint256,uint24,address,uint256,bytes32,bytes32,address)', - ).substring(2), - 'hex', - ), - wethCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(wethAmountToBridge).toBuffer(), - new Fr(uniswapFeeTier).toBuffer(), - daiCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(minimumOutputAmount).toBuffer(), - secretHashForRedeemingDai.toBuffer(), - secretHashForDepositingSwappedDai.toBuffer(), - ownerEthAddress.toBuffer32(), - ]), - ), - )[0]; - - const swapPrivateLeaf = toTruncField( - sha256( - Buffer.concat([ - uniswapL2Contract.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - EthAddress.fromString(uniswapPortal.address).toBuffer32(), - new Fr(publicClient.chain.id).toBuffer(), // chain id - swapPrivateContent.toBuffer(), - ]), - ), - )[0]; - - const withdrawContent = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), - uniswapPortalAddress.toBuffer32(), - new Fr(wethAmountToBridge).toBuffer(), - uniswapPortalAddress.toBuffer32(), - ]), - ), - )[0]; - - const withdrawLeaf = toTruncField( - sha256( - Buffer.concat([ - wethCrossChainHarness.l2Bridge.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - wethCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(publicClient.chain.id).toBuffer(), // chain id - withdrawContent.toBuffer(), - ]), - ), - )[0]; + const swapPrivateContent = sha256ToField( + Buffer.concat([ + Buffer.from( + toFunctionSelector( + 'swap_private(address,uint256,uint24,address,uint256,bytes32,bytes32,address)', + ).substring(2), + 'hex', + ), + wethCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(wethAmountToBridge).toBuffer(), + new Fr(uniswapFeeTier).toBuffer(), + daiCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(minimumOutputAmount).toBuffer(), + secretHashForRedeemingDai.toBuffer(), + secretHashForDepositingSwappedDai.toBuffer(), + ownerEthAddress.toBuffer32(), + ]), + ); + + const swapPrivateLeaf = sha256ToField( + Buffer.concat([ + uniswapL2Contract.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + EthAddress.fromString(uniswapPortal.address).toBuffer32(), + new Fr(publicClient.chain.id).toBuffer(), // chain id + swapPrivateContent.toBuffer(), + ]), + ); + + const withdrawContent = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), + uniswapPortalAddress.toBuffer32(), + new Fr(wethAmountToBridge).toBuffer(), + uniswapPortalAddress.toBuffer32(), + ]), + ); + + const withdrawLeaf = sha256ToField( + Buffer.concat([ + wethCrossChainHarness.l2Bridge.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + wethCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(publicClient.chain.id).toBuffer(), // chain id + withdrawContent.toBuffer(), + ]), + ); // ensure that user's funds were burnt await wethCrossChainHarness.expectPrivateBalanceOnL2(ownerAddress, wethL2BalanceBeforeSwap - wethAmountToBridge); @@ -483,61 +474,53 @@ export const uniswapL1L2TestSuite = ( // 4.2 Call swap_public from user2 on behalf of owner const uniswapL2Interaction = await action.send().wait(); - const swapPublicContent = toTruncField( - sha256( - Buffer.concat([ - Buffer.from( - toFunctionSelector( - 'swap_public(address,uint256,uint24,address,uint256,bytes32,bytes32,address)', - ).substring(2), - 'hex', + const swapPublicContent = sha256ToField( + Buffer.concat([ + Buffer.from( + toFunctionSelector('swap_public(address,uint256,uint24,address,uint256,bytes32,bytes32,address)').substring( + 2, ), - wethCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(wethAmountToBridge).toBuffer(), - new Fr(uniswapFeeTier).toBuffer(), - daiCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(minimumOutputAmount).toBuffer(), - ownerAddress.toBuffer(), - secretHashForDepositingSwappedDai.toBuffer(), - ownerEthAddress.toBuffer32(), - ]), - ), - )[0]; - - const swapPublicLeaf = toTruncField( - sha256( - Buffer.concat([ - uniswapL2Contract.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - EthAddress.fromString(uniswapPortal.address).toBuffer32(), - new Fr(publicClient.chain.id).toBuffer(), // chain id - swapPublicContent.toBuffer(), - ]), - ), - )[0]; - - const withdrawContent = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), - uniswapPortalAddress.toBuffer32(), - new Fr(wethAmountToBridge).toBuffer(), - uniswapPortalAddress.toBuffer32(), - ]), - ), - )[0]; - - const withdrawLeaf = toTruncField( - sha256( - Buffer.concat([ - wethCrossChainHarness.l2Bridge.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - wethCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(publicClient.chain.id).toBuffer(), // chain id - withdrawContent.toBuffer(), - ]), - ), - )[0]; + 'hex', + ), + wethCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(wethAmountToBridge).toBuffer(), + new Fr(uniswapFeeTier).toBuffer(), + daiCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(minimumOutputAmount).toBuffer(), + ownerAddress.toBuffer(), + secretHashForDepositingSwappedDai.toBuffer(), + ownerEthAddress.toBuffer32(), + ]), + ); + + const swapPublicLeaf = sha256ToField( + Buffer.concat([ + uniswapL2Contract.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + EthAddress.fromString(uniswapPortal.address).toBuffer32(), + new Fr(publicClient.chain.id).toBuffer(), // chain id + swapPublicContent.toBuffer(), + ]), + ); + + const withdrawContent = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), + uniswapPortalAddress.toBuffer32(), + new Fr(wethAmountToBridge).toBuffer(), + uniswapPortalAddress.toBuffer32(), + ]), + ); + + const withdrawLeaf = sha256ToField( + Buffer.concat([ + wethCrossChainHarness.l2Bridge.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + wethCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(publicClient.chain.id).toBuffer(), // chain id + withdrawContent.toBuffer(), + ]), + ); // check weth balance of owner on L2 (we first bridged `wethAmountToBridge` into L2 and now withdrew it!) await wethCrossChainHarness.expectPublicBalanceOnL2(ownerAddress, wethL2BalanceBeforeSwap - wethAmountToBridge); @@ -863,61 +846,53 @@ export const uniswapL1L2TestSuite = ( .send() .wait(); - const swapPrivateContent = toTruncField( - sha256( - Buffer.concat([ - Buffer.from( - toFunctionSelector( - 'swap_private(address,uint256,uint24,address,uint256,bytes32,bytes32,address)', - ).substring(2), - 'hex', - ), - wethCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(wethAmountToBridge).toBuffer(), - new Fr(uniswapFeeTier).toBuffer(), - daiCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(minimumOutputAmount).toBuffer(), - secretHashForRedeemingDai.toBuffer(), - secretHashForDepositingSwappedDai.toBuffer(), - ownerEthAddress.toBuffer32(), - ]), - ), - )[0]; - - const swapPrivateLeaf = toTruncField( - sha256( - Buffer.concat([ - uniswapL2Contract.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - EthAddress.fromString(uniswapPortal.address).toBuffer32(), - new Fr(publicClient.chain.id).toBuffer(), // chain id - swapPrivateContent.toBuffer(), - ]), - ), - )[0]; - - const withdrawContent = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), - uniswapPortalAddress.toBuffer32(), - new Fr(wethAmountToBridge).toBuffer(), - uniswapPortalAddress.toBuffer32(), - ]), - ), - )[0]; - - const withdrawLeaf = toTruncField( - sha256( - Buffer.concat([ - wethCrossChainHarness.l2Bridge.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - wethCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(publicClient.chain.id).toBuffer(), // chain id - withdrawContent.toBuffer(), - ]), - ), - )[0]; + const swapPrivateContent = sha256ToField( + Buffer.concat([ + Buffer.from( + toFunctionSelector( + 'swap_private(address,uint256,uint24,address,uint256,bytes32,bytes32,address)', + ).substring(2), + 'hex', + ), + wethCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(wethAmountToBridge).toBuffer(), + new Fr(uniswapFeeTier).toBuffer(), + daiCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(minimumOutputAmount).toBuffer(), + secretHashForRedeemingDai.toBuffer(), + secretHashForDepositingSwappedDai.toBuffer(), + ownerEthAddress.toBuffer32(), + ]), + ); + + const swapPrivateLeaf = sha256ToField( + Buffer.concat([ + uniswapL2Contract.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + EthAddress.fromString(uniswapPortal.address).toBuffer32(), + new Fr(publicClient.chain.id).toBuffer(), // chain id + swapPrivateContent.toBuffer(), + ]), + ); + + const withdrawContent = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), + uniswapPortalAddress.toBuffer32(), + new Fr(wethAmountToBridge).toBuffer(), + uniswapPortalAddress.toBuffer32(), + ]), + ); + + const withdrawLeaf = sha256ToField( + Buffer.concat([ + wethCrossChainHarness.l2Bridge.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + wethCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(publicClient.chain.id).toBuffer(), // chain id + withdrawContent.toBuffer(), + ]), + ); const [swapPrivateL2MessageIndex, swapPrivateSiblingPath] = await aztecNode.getL2ToL1MessageMembershipWitness( withdrawReceipt.blockNumber!, @@ -1002,61 +977,53 @@ export const uniswapL1L2TestSuite = ( .send() .wait(); - const swapPublicContent = toTruncField( - sha256( - Buffer.concat([ - Buffer.from( - toFunctionSelector( - 'swap_public(address,uint256,uint24,address,uint256,bytes32,bytes32,address)', - ).substring(2), - 'hex', + const swapPublicContent = sha256ToField( + Buffer.concat([ + Buffer.from( + toFunctionSelector('swap_public(address,uint256,uint24,address,uint256,bytes32,bytes32,address)').substring( + 2, ), - wethCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(wethAmountToBridge).toBuffer(), - new Fr(uniswapFeeTier).toBuffer(), - daiCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(minimumOutputAmount).toBuffer(), - ownerAddress.toBuffer(), - secretHashForDepositingSwappedDai.toBuffer(), - ownerEthAddress.toBuffer32(), - ]), - ), - )[0]; - - const swapPublicLeaf = toTruncField( - sha256( - Buffer.concat([ - uniswapL2Contract.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - EthAddress.fromString(uniswapPortal.address).toBuffer32(), - new Fr(publicClient.chain.id).toBuffer(), // chain id - swapPublicContent.toBuffer(), - ]), - ), - )[0]; - - const withdrawContent = toTruncField( - sha256( - Buffer.concat([ - Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), - uniswapPortalAddress.toBuffer32(), - new Fr(wethAmountToBridge).toBuffer(), - uniswapPortalAddress.toBuffer32(), - ]), - ), - )[0]; - - const withdrawLeaf = toTruncField( - sha256( - Buffer.concat([ - wethCrossChainHarness.l2Bridge.address.toBuffer(), - new Fr(1).toBuffer(), // aztec version - wethCrossChainHarness.tokenPortalAddress.toBuffer32(), - new Fr(publicClient.chain.id).toBuffer(), // chain id - withdrawContent.toBuffer(), - ]), - ), - )[0]; + 'hex', + ), + wethCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(wethAmountToBridge).toBuffer(), + new Fr(uniswapFeeTier).toBuffer(), + daiCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(minimumOutputAmount).toBuffer(), + ownerAddress.toBuffer(), + secretHashForDepositingSwappedDai.toBuffer(), + ownerEthAddress.toBuffer32(), + ]), + ); + + const swapPublicLeaf = sha256ToField( + Buffer.concat([ + uniswapL2Contract.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + EthAddress.fromString(uniswapPortal.address).toBuffer32(), + new Fr(publicClient.chain.id).toBuffer(), // chain id + swapPublicContent.toBuffer(), + ]), + ); + + const withdrawContent = sha256ToField( + Buffer.concat([ + Buffer.from(toFunctionSelector('withdraw(address,uint256,address)').substring(2), 'hex'), + uniswapPortalAddress.toBuffer32(), + new Fr(wethAmountToBridge).toBuffer(), + uniswapPortalAddress.toBuffer32(), + ]), + ); + + const withdrawLeaf = sha256ToField( + Buffer.concat([ + wethCrossChainHarness.l2Bridge.address.toBuffer(), + new Fr(1).toBuffer(), // aztec version + wethCrossChainHarness.tokenPortalAddress.toBuffer32(), + new Fr(publicClient.chain.id).toBuffer(), // chain id + withdrawContent.toBuffer(), + ]), + ); const [swapPublicL2MessageIndex, swapPublicSiblingPath] = await aztecNode.getL2ToL1MessageMembershipWitness( withdrawReceipt.blockNumber!, diff --git a/yarn-project/foundation/src/crypto/sha256/index.ts b/yarn-project/foundation/src/crypto/sha256/index.ts index 6054e78e834..0f6fb259e85 100644 --- a/yarn-project/foundation/src/crypto/sha256/index.ts +++ b/yarn-project/foundation/src/crypto/sha256/index.ts @@ -1,3 +1,10 @@ import { default as hash } from 'hash.js'; +import { Fr } from '../../fields/fields.js'; +import { truncateAndPad } from '../../serialize/free_funcs.js'; + export const sha256 = (data: Buffer) => Buffer.from(hash.sha256().update(data).digest()); + +export const sha256Trunc = (data: Buffer) => truncateAndPad(sha256(data)); + +export const sha256ToField = (data: Buffer) => Fr.fromBuffer(sha256Trunc(data)); diff --git a/yarn-project/foundation/src/serialize/free_funcs.test.ts b/yarn-project/foundation/src/serialize/free_funcs.test.ts index 1f6133683e4..b7778fffac1 100644 --- a/yarn-project/foundation/src/serialize/free_funcs.test.ts +++ b/yarn-project/foundation/src/serialize/free_funcs.test.ts @@ -21,7 +21,7 @@ describe('buffer to fields and back', () => { const originalBuffer = Buffer.concat([Buffer.alloc(1), randomBytes(31)]); // Serialize the buffer to one field - const field = toTruncField(originalBuffer)[0]; + const field = toTruncField(originalBuffer); // Deserialize the field back to a buffer const reconstructedBuffer = fromTruncField(field); diff --git a/yarn-project/foundation/src/serialize/free_funcs.ts b/yarn-project/foundation/src/serialize/free_funcs.ts index 17260de202e..319112ebede 100644 --- a/yarn-project/foundation/src/serialize/free_funcs.ts +++ b/yarn-project/foundation/src/serialize/free_funcs.ts @@ -165,14 +165,16 @@ export function truncateAndPad(buf: Buffer): Buffer { * @param buf - 32 or 31 bytes of data * @returns 1 field element */ -export function toTruncField(buf: Buffer): [Fr] { +export function toTruncField(buf: Buffer): Fr { if (buf.length !== 32 && buf.length !== 31) { throw new Error('Buffer must be 31 or 32 bytes'); } if ((buf.length == 32 && buf[0] == 0) || buf.length == 31) { - return [Fr.fromBuffer(buf)]; + return Fr.fromBuffer(buf); } else { - return [Fr.fromBuffer(buf.subarray(0, 31))]; + // Note: safer to NOT truncate here, all inputs are expected to be truncated + // from Noir or L1 Contracts or Class.hash() methods + throw new Error(`Number ${toBigInt(buf)} does not fit in 31 byte truncated buffer`); } } diff --git a/yarn-project/noir-protocol-circuits-types/src/__snapshots__/index.test.ts.snap b/yarn-project/noir-protocol-circuits-types/src/__snapshots__/index.test.ts.snap index 63667396b24..192a72b643a 100644 --- a/yarn-project/noir-protocol-circuits-types/src/__snapshots__/index.test.ts.snap +++ b/yarn-project/noir-protocol-circuits-types/src/__snapshots__/index.test.ts.snap @@ -82,9 +82,7 @@ PrivateKernelInnerCircuitPublicInputs { }, "end": CombinedAccumulatedData { "encryptedLogPreimagesLength": Fr<0x000000000000000000000000000000000000000000000000000000000000000c>, - "encryptedLogsHash": [ - Fr<0x00f33ae280239814c4dfaaafc16fc138a8d3eae52bb962af6576cbb61c2af246>, - ], + "encryptedLogsHash": Fr<0x00f33ae280239814c4dfaaafc16fc138a8d3eae52bb962af6576cbb61c2af246>, "newL2ToL1Msgs": [ Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, @@ -999,9 +997,7 @@ PrivateKernelInnerCircuitPublicInputs { "code": 0, }, "unencryptedLogPreimagesLength": Fr<0x000000000000000000000000000000000000000000000000000000000000000c>, - "unencryptedLogsHash": [ - Fr<0x00f33ae280239814c4dfaaafc16fc138a8d3eae52bb962af6576cbb61c2af246>, - ], + "unencryptedLogsHash": Fr<0x00f33ae280239814c4dfaaafc16fc138a8d3eae52bb962af6576cbb61c2af246>, }, "isPrivate": true, "minRevertibleSideEffectCounter": Fr<0x0000000000000000000000000000000000000000000000000000000000000002>, @@ -1896,9 +1892,7 @@ PrivateKernelTailCircuitPublicInputs { }, "end": PrivateAccumulatedRevertibleData { "encryptedLogPreimagesLength": Fr<0x0000000000000000000000000000000000000000000000000000000000000138>, - "encryptedLogsHash": [ - Fr<0x0003100e66eb6812178264cd03595ddc65ec007a177d3b06abc1d8fc27357eca>, - ], + "encryptedLogsHash": Fr<0x0003100e66eb6812178264cd03595ddc65ec007a177d3b06abc1d8fc27357eca>, "newL2ToL1Msgs": [ Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, Fr<0x0000000000000000000000000000000000000000000000000000000000000000>, @@ -2546,9 +2540,7 @@ PrivateKernelTailCircuitPublicInputs { }, ], "unencryptedLogPreimagesLength": Fr<0x0000000000000000000000000000000000000000000000000000000000000004>, - "unencryptedLogsHash": [ - Fr<0x006003947a07e21c81ce2062539d6d6864fe999b58b03fc46f6c190d9eac9b39>, - ], + "unencryptedLogsHash": Fr<0x006003947a07e21c81ce2062539d6d6864fe999b58b03fc46f6c190d9eac9b39>, }, "endNonRevertibleData": PrivateAccumulatedNonRevertibleData { "newNoteHashes": [ diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 121f3acc501..62622e993a0 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -49,7 +49,6 @@ import { MergeRollupInputs, NULLIFIER_TREE_HEIGHT, NUM_BYTES_PER_SHA256, - NUM_FIELDS_PER_SHA256, NonMembershipHint, NoteHashReadRequestMembershipWitness, NullifierKeyValidationRequest, @@ -199,7 +198,6 @@ import { ConstantRollupData as ConstantRollupDataNoir, ContentCommitment as ContentCommitmentNoir, Field, - FixedLengthArray, GlobalVariables as GlobalVariablesNoir, Header as HeaderNoir, ParityPublicInputs as ParityPublicInputsNoir, @@ -701,8 +699,8 @@ export function mapPrivateCircuitPublicInputsToNoir( new_l2_to_l1_msgs: mapTuple(privateCircuitPublicInputs.newL2ToL1Msgs, mapL2ToL1MessageToNoir), start_side_effect_counter: mapFieldToNoir(privateCircuitPublicInputs.startSideEffectCounter), end_side_effect_counter: mapFieldToNoir(privateCircuitPublicInputs.endSideEffectCounter), - encrypted_logs_hash: mapTuple(privateCircuitPublicInputs.encryptedLogsHash, mapFieldToNoir), - unencrypted_logs_hash: mapTuple(privateCircuitPublicInputs.unencryptedLogsHash, mapFieldToNoir), + encrypted_logs_hash: mapFieldToNoir(privateCircuitPublicInputs.encryptedLogsHash), + unencrypted_logs_hash: mapFieldToNoir(privateCircuitPublicInputs.unencryptedLogsHash), encrypted_log_preimages_length: mapFieldToNoir(privateCircuitPublicInputs.encryptedLogPreimagesLength), unencrypted_log_preimages_length: mapFieldToNoir(privateCircuitPublicInputs.unencryptedLogPreimagesLength), historical_header: mapHeaderToNoir(privateCircuitPublicInputs.historicalHeader), @@ -815,8 +813,8 @@ export function mapTupleFromNoir( * @param hash - The hash as it is represented in Noir (1 fields). * @returns The hash represented as a 31 bytes long buffer. */ -export function mapSha256HashFromNoir(hash: FixedLengthArray): Buffer { - return Buffer.concat(hash.map(mapFieldFromNoir).map(fr => toBufferBE(fr.toBigInt(), NUM_BYTES_PER_SHA256))); +export function mapSha256HashFromNoir(hash: Field): Buffer { + return toBufferBE(mapFieldFromNoir(hash).toBigInt(), NUM_BYTES_PER_SHA256); } /** @@ -824,8 +822,8 @@ export function mapSha256HashFromNoir(hash: FixedLengthArray { - return toTruncField(hash).map(mapFieldToNoir) as FixedLengthArray; +export function mapSha256HashToNoir(hash: Buffer): Field { + return mapFieldToNoir(toTruncField(hash)); } /** @@ -1019,8 +1017,8 @@ export function mapCombinedAccumulatedDataFromNoir( mapCallRequestFromNoir, ), mapTupleFromNoir(combinedAccumulatedData.new_l2_to_l1_msgs, MAX_NEW_L2_TO_L1_MSGS_PER_TX, mapFieldFromNoir), - mapTupleFromNoir(combinedAccumulatedData.encrypted_logs_hash, NUM_FIELDS_PER_SHA256, mapFieldFromNoir), - mapTupleFromNoir(combinedAccumulatedData.unencrypted_logs_hash, NUM_FIELDS_PER_SHA256, mapFieldFromNoir), + mapFieldFromNoir(combinedAccumulatedData.encrypted_logs_hash), + mapFieldFromNoir(combinedAccumulatedData.unencrypted_logs_hash), mapFieldFromNoir(combinedAccumulatedData.encrypted_log_preimages_length), mapFieldFromNoir(combinedAccumulatedData.unencrypted_log_preimages_length), mapTupleFromNoir( @@ -1057,8 +1055,8 @@ export function mapFinalAccumulatedDataFromNoir( mapCallRequestFromNoir, ), mapTupleFromNoir(finalAccumulatedData.new_l2_to_l1_msgs, MAX_NEW_L2_TO_L1_MSGS_PER_TX, mapFieldFromNoir), - mapTupleFromNoir(finalAccumulatedData.encrypted_logs_hash, NUM_FIELDS_PER_SHA256, mapFieldFromNoir), - mapTupleFromNoir(finalAccumulatedData.unencrypted_logs_hash, NUM_FIELDS_PER_SHA256, mapFieldFromNoir), + mapFieldFromNoir(finalAccumulatedData.encrypted_logs_hash), + mapFieldFromNoir(finalAccumulatedData.unencrypted_logs_hash), mapFieldFromNoir(finalAccumulatedData.encrypted_log_preimages_length), mapFieldFromNoir(finalAccumulatedData.unencrypted_log_preimages_length), ); @@ -1113,8 +1111,8 @@ export function mapPrivateAccumulatedRevertibleDataToNoir( private_call_stack: mapTuple(data.privateCallStack, mapCallRequestToNoir), public_call_stack: mapTuple(data.publicCallStack, mapCallRequestToNoir), new_l2_to_l1_msgs: mapTuple(data.newL2ToL1Msgs, mapFieldToNoir), - encrypted_logs_hash: mapTuple(data.encryptedLogsHash, mapFieldToNoir), - unencrypted_logs_hash: mapTuple(data.unencryptedLogsHash, mapFieldToNoir), + encrypted_logs_hash: mapFieldToNoir(data.encryptedLogsHash), + unencrypted_logs_hash: mapFieldToNoir(data.unencryptedLogsHash), encrypted_log_preimages_length: mapFieldToNoir(data.encryptedLogPreimagesLength), unencrypted_log_preimages_length: mapFieldToNoir(data.unencryptedLogPreimagesLength), }; @@ -1135,8 +1133,8 @@ export function mapCombinedAccumulatedDataToNoir( private_call_stack: mapTuple(combinedAccumulatedData.privateCallStack, mapCallRequestToNoir), public_call_stack: mapTuple(combinedAccumulatedData.publicCallStack, mapCallRequestToNoir), new_l2_to_l1_msgs: mapTuple(combinedAccumulatedData.newL2ToL1Msgs, mapFieldToNoir), - encrypted_logs_hash: mapTuple(combinedAccumulatedData.encryptedLogsHash, mapFieldToNoir), - unencrypted_logs_hash: mapTuple(combinedAccumulatedData.unencryptedLogsHash, mapFieldToNoir), + encrypted_logs_hash: mapFieldToNoir(combinedAccumulatedData.encryptedLogsHash), + unencrypted_logs_hash: mapFieldToNoir(combinedAccumulatedData.unencryptedLogsHash), encrypted_log_preimages_length: mapFieldToNoir(combinedAccumulatedData.encryptedLogPreimagesLength), unencrypted_log_preimages_length: mapFieldToNoir(combinedAccumulatedData.unencryptedLogPreimagesLength), public_data_update_requests: mapTuple( @@ -1220,8 +1218,8 @@ export function mapPublicAccumulatedRevertibleDataToNoir( private_call_stack: mapTuple(data.privateCallStack, mapCallRequestToNoir), public_call_stack: mapTuple(data.publicCallStack, mapCallRequestToNoir), new_l2_to_l1_msgs: mapTuple(data.newL2ToL1Msgs, mapFieldToNoir), - encrypted_logs_hash: mapTuple(data.encryptedLogsHash, mapFieldToNoir), - unencrypted_logs_hash: mapTuple(data.unencryptedLogsHash, mapFieldToNoir), + encrypted_logs_hash: mapFieldToNoir(data.encryptedLogsHash), + unencrypted_logs_hash: mapFieldToNoir(data.unencryptedLogsHash), encrypted_log_preimages_length: mapFieldToNoir(data.encryptedLogPreimagesLength), unencrypted_log_preimages_length: mapFieldToNoir(data.unencryptedLogPreimagesLength), public_data_update_requests: mapTuple(data.publicDataUpdateRequests, mapPublicDataUpdateRequestToNoir), @@ -1322,8 +1320,8 @@ export function mapFinalAccumulatedDataToNoir( private_call_stack: mapTuple(finalAccumulatedData.privateCallStack, mapCallRequestToNoir), public_call_stack: mapTuple(finalAccumulatedData.publicCallStack, mapCallRequestToNoir), new_l2_to_l1_msgs: mapTuple(finalAccumulatedData.newL2ToL1Msgs, mapFieldToNoir), - encrypted_logs_hash: mapTuple(finalAccumulatedData.encryptedLogsHash, mapFieldToNoir), - unencrypted_logs_hash: mapTuple(finalAccumulatedData.unencryptedLogsHash, mapFieldToNoir), + encrypted_logs_hash: mapFieldToNoir(finalAccumulatedData.encryptedLogsHash), + unencrypted_logs_hash: mapFieldToNoir(finalAccumulatedData.unencryptedLogsHash), encrypted_log_preimages_length: mapFieldToNoir(finalAccumulatedData.encryptedLogPreimagesLength), unencrypted_log_preimages_length: mapFieldToNoir(finalAccumulatedData.unencryptedLogPreimagesLength), }; @@ -1460,8 +1458,8 @@ export function mapPublicAccumulatedRevertibleDataFromNoir( mapTupleFromNoir(data.private_call_stack, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, mapCallRequestFromNoir), mapTupleFromNoir(data.public_call_stack, MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, mapCallRequestFromNoir), mapTupleFromNoir(data.new_l2_to_l1_msgs, MAX_NEW_L2_TO_L1_MSGS_PER_TX, mapFieldFromNoir), - mapTupleFromNoir(data.encrypted_logs_hash, NUM_FIELDS_PER_SHA256, mapFieldFromNoir), - mapTupleFromNoir(data.unencrypted_logs_hash, NUM_FIELDS_PER_SHA256, mapFieldFromNoir), + mapFieldFromNoir(data.encrypted_logs_hash), + mapFieldFromNoir(data.unencrypted_logs_hash), mapFieldFromNoir(data.encrypted_log_preimages_length), mapFieldFromNoir(data.unencrypted_log_preimages_length), mapTupleFromNoir( @@ -1569,7 +1567,7 @@ export function mapPublicCircuitPublicInputsToNoir( new_l2_to_l1_msgs: mapTuple(publicInputs.newL2ToL1Msgs, mapL2ToL1MessageToNoir), start_side_effect_counter: mapFieldToNoir(publicInputs.startSideEffectCounter), end_side_effect_counter: mapFieldToNoir(publicInputs.endSideEffectCounter), - unencrypted_logs_hash: mapTuple(publicInputs.unencryptedLogsHash, mapFieldToNoir), + unencrypted_logs_hash: mapFieldToNoir(publicInputs.unencryptedLogsHash), unencrypted_log_preimages_length: mapFieldToNoir(publicInputs.unencryptedLogPreimagesLength), historical_header: mapHeaderToNoir(publicInputs.historicalHeader), @@ -1608,8 +1606,8 @@ export function mapBaseOrMergeRollupPublicInputsToNoir( constants: mapConstantRollupDataToNoir(baseOrMergeRollupPublicInputs.constants), start: mapPartialStateReferenceToNoir(baseOrMergeRollupPublicInputs.start), end: mapPartialStateReferenceToNoir(baseOrMergeRollupPublicInputs.end), - txs_effects_hash: mapTuple(baseOrMergeRollupPublicInputs.txsEffectsHash, mapFieldToNoir), - out_hash: mapTuple(baseOrMergeRollupPublicInputs.outHash, mapFieldToNoir), + txs_effects_hash: mapFieldToNoir(baseOrMergeRollupPublicInputs.txsEffectsHash), + out_hash: mapFieldToNoir(baseOrMergeRollupPublicInputs.outHash), }; } @@ -1656,8 +1654,8 @@ export function mapBaseOrMergeRollupPublicInputsFromNoir( mapConstantRollupDataFromNoir(baseOrMergeRollupPublicInputs.constants), mapPartialStateReferenceFromNoir(baseOrMergeRollupPublicInputs.start), mapPartialStateReferenceFromNoir(baseOrMergeRollupPublicInputs.end), - mapTupleFromNoir(baseOrMergeRollupPublicInputs.txs_effects_hash, NUM_FIELDS_PER_SHA256, mapFieldFromNoir), - mapTupleFromNoir(baseOrMergeRollupPublicInputs.out_hash, NUM_FIELDS_PER_SHA256, mapFieldFromNoir), + mapFieldFromNoir(baseOrMergeRollupPublicInputs.txs_effects_hash), + mapFieldFromNoir(baseOrMergeRollupPublicInputs.out_hash), ); } diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.test.ts index 73a2bd9fe2d..8d11730cb78 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.test.ts @@ -45,7 +45,6 @@ import { } from '@aztec/circuits.js/testing'; import { makeTuple, range } from '@aztec/foundation/array'; import { padArrayEnd, times } from '@aztec/foundation/collection'; -import { toTruncField } from '@aztec/foundation/serialize'; import { sleep } from '@aztec/foundation/sleep'; import { openTmpStore } from '@aztec/kv-store/utils'; import { WASMSimulator } from '@aztec/simulator'; @@ -365,8 +364,8 @@ describe('prover/tx-prover', () => { processedTx.data.end.newNullifiers[tx.data.end.newNullifiers.length - 1] = SideEffectLinkedToNoteHash.empty(); processedTx.data.end.newL2ToL1Msgs = makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_TX, fr, seed + 0x300); - processedTx.data.end.encryptedLogsHash = toTruncField(processedTx.encryptedLogs.hash()); - processedTx.data.end.unencryptedLogsHash = toTruncField(processedTx.unencryptedLogs.hash()); + processedTx.data.end.encryptedLogsHash = Fr.fromBuffer(processedTx.encryptedLogs.hash()); + processedTx.data.end.unencryptedLogsHash = Fr.fromBuffer(processedTx.unencryptedLogs.hash()); return processedTx; }; diff --git a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts index 0ecd9e56cae..46474f870ae 100644 --- a/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts +++ b/yarn-project/sequencer-client/src/sequencer/abstract_phase_manager.ts @@ -44,7 +44,7 @@ import { import { computeVarArgsHash } from '@aztec/circuits.js/hash'; import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import { Tuple, toTruncField } from '@aztec/foundation/serialize'; +import { Tuple } from '@aztec/foundation/serialize'; import { PublicExecution, PublicExecutionResult, @@ -348,7 +348,7 @@ export abstract class AbstractPhaseManager { ); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) --> set this in Noir - const unencryptedLogsHash = toTruncField(result.unencryptedLogs.hash()); + const unencryptedLogsHash = Fr.fromBuffer(result.unencryptedLogs.hash()); const unencryptedLogPreimagesLength = new Fr(result.unencryptedLogs.getSerializedLength()); return PublicCircuitPublicInputs.from({ diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts index 2f433253f25..38be5b440ca 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -99,7 +99,7 @@ describe('public_processor', () => { const includeLogs = false; const tx = mockTx(seed, includeLogs); tx.data.end.publicCallStack = makeTuple(MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty); - tx.data.end.unencryptedLogsHash = [Fr.ZERO]; + tx.data.end.unencryptedLogsHash = Fr.ZERO; tx.data.endNonRevertibleData.publicCallStack = makeTuple( MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty, @@ -204,7 +204,7 @@ describe('public_processor', () => { MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty, ); - kernelOutput.end.unencryptedLogsHash = [Fr.ZERO]; + kernelOutput.end.unencryptedLogsHash = Fr.ZERO; const tx = new Tx(kernelOutput, proof, TxL2Logs.empty(), TxL2Logs.empty(), publicCallRequests); @@ -247,7 +247,7 @@ describe('public_processor', () => { MAX_NON_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty, ); - kernelOutput.end.unencryptedLogsHash = [Fr.ZERO]; + kernelOutput.end.unencryptedLogsHash = Fr.ZERO; kernelOutput.needsSetup = false; kernelOutput.needsTeardown = false; @@ -291,7 +291,7 @@ describe('public_processor', () => { callRequests[2].callContext.sideEffectCounter = 4; const kernelOutput = makePrivateKernelTailCircuitPublicInputs(0x10); - kernelOutput.end.unencryptedLogsHash = [Fr.ZERO]; + kernelOutput.end.unencryptedLogsHash = Fr.ZERO; addKernelPublicCallStack(kernelOutput, { setupCalls: [callRequests[0]], @@ -407,7 +407,7 @@ describe('public_processor', () => { callRequests[2].callContext.sideEffectCounter = 4; const kernelOutput = makePrivateKernelTailCircuitPublicInputs(0x10); - kernelOutput.end.unencryptedLogsHash = [Fr.ZERO]; + kernelOutput.end.unencryptedLogsHash = Fr.ZERO; addKernelPublicCallStack(kernelOutput, { setupCalls: [callRequests[0]], @@ -511,7 +511,7 @@ describe('public_processor', () => { callRequests[2].callContext.sideEffectCounter = 4; const kernelOutput = makePrivateKernelTailCircuitPublicInputs(0x10); - kernelOutput.end.unencryptedLogsHash = [Fr.ZERO]; + kernelOutput.end.unencryptedLogsHash = Fr.ZERO; addKernelPublicCallStack(kernelOutput, { setupCalls: [callRequests[0]], @@ -615,7 +615,7 @@ describe('public_processor', () => { const kernelOutput = makePrivateKernelTailCircuitPublicInputs(0x10); - kernelOutput.end.unencryptedLogsHash = [Fr.ZERO]; + kernelOutput.end.unencryptedLogsHash = Fr.ZERO; addKernelPublicCallStack(kernelOutput, { setupCalls: [callRequests[0]], appLogicCalls: [callRequests[2]], diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index 1491b7d7dcb..b5698d2dea7 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -3,7 +3,6 @@ import { FunctionArtifactWithDebugMetadata, decodeReturnValues } from '@aztec/fo import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { toTruncField } from '@aztec/foundation/serialize'; import { extractReturnWitness } from '../acvm/deserialize.js'; import { Oracle, acvm, extractCallStack } from '../acvm/index.js'; @@ -47,9 +46,9 @@ export async function executePrivateFunction( const encryptedLogs = context.getEncryptedLogs(); const unencryptedLogs = context.getUnencryptedLogs(); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) --> set this in Noir - publicInputs.encryptedLogsHash = toTruncField(encryptedLogs.hash()); + publicInputs.encryptedLogsHash = Fr.fromBuffer(encryptedLogs.hash()); publicInputs.encryptedLogPreimagesLength = new Fr(encryptedLogs.getSerializedLength()); - publicInputs.unencryptedLogsHash = toTruncField(unencryptedLogs.hash()); + publicInputs.unencryptedLogsHash = Fr.fromBuffer(unencryptedLogs.hash()); publicInputs.unencryptedLogPreimagesLength = new Fr(unencryptedLogs.getSerializedLength()); const callStackItem = new PrivateCallStackItem(contractAddress, functionData, publicInputs); diff --git a/yarn-project/simulator/src/test/utils.ts b/yarn-project/simulator/src/test/utils.ts index 081e4320a53..f8f5b6b4166 100644 --- a/yarn-project/simulator/src/test/utils.ts +++ b/yarn-project/simulator/src/test/utils.ts @@ -1,8 +1,7 @@ import { L1Actor, L1ToL2Message, L2Actor } from '@aztec/circuit-types'; import { AztecAddress, EthAddress, Fr } from '@aztec/circuits.js'; import { computeMessageSecretHash } from '@aztec/circuits.js/hash'; -import { sha256 } from '@aztec/foundation/crypto'; -import { toTruncField } from '@aztec/foundation/serialize'; +import { sha256ToField } from '@aztec/foundation/crypto'; /** * Test utility function to craft an L1 to L2 message. @@ -22,7 +21,7 @@ export const buildL1ToL2Message = ( const selectorBuf = Buffer.from(selector, 'hex'); const contentBuf = Buffer.concat([selectorBuf, ...contentPreimage.map(field => field.toBuffer())]); - const content = toTruncField(sha256(contentBuf))[0]; + const content = sha256ToField(contentBuf); const secretHash = computeMessageSecretHash(secret); // Eventually the kernel will need to prove the kernel portal pair exists within the contract tree,