diff --git a/Cargo.lock b/Cargo.lock index 7d47805aab..33034aa289 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,6 +150,45 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556cabf32c649f5d6ccfbcc5bdf9f5a7cc4aa1eb5043cdabedff3fd49dd2cab9" +[[package]] +name = "ark-bls12-377" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.4.2" @@ -193,17 +232,42 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + [[package]] name = "ark-serialize" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ + "ark-serialize-derive", "ark-std", "digest 0.10.7", "num-bigint", ] +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-std" version = "0.4.0" @@ -2992,6 +3056,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gnark-mimc" +version = "0.1.0" +dependencies = [ + "ark-bls12-377", + "ark-bn254", + "ark-ff", + "num-bigint", + "serde", + "serde-utils", + "serde_json", + "sha3", + "thiserror", + "tiny-keccak", + "unionlabs", +] + [[package]] name = "group" version = "0.13.0" @@ -3747,6 +3828,65 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linea-light-client" +version = "0.1.0" +dependencies = [ + "base64 0.21.7", + "cosmwasm-std", + "ethereum-verifier", + "ethers-core", + "gnark-mimc", + "hex", + "ics008-wasm-client", + "linea-verifier", + "linea-zktrie", + "protos", + "rlp", + "schemars", + "serde", + "serde-json-wasm 1.0.1", + "serde_json", + "sha3", + "thiserror", + "tiny-keccak", + "unionlabs", +] + +[[package]] +name = "linea-verifier" +version = "0.1.0" +dependencies = [ + "ethereum-verifier", + "ethers-core", + "gnark-mimc", + "hex", + "hex-literal", + "linea-zktrie", + "rlp", + "scroll-codec", + "serde", + "serde-utils", + "serde_json", + "sha3", + "thiserror", + "unionlabs", +] + +[[package]] +name = "linea-zktrie" +version = "0.1.0" +dependencies = [ + "gnark-mimc", + "hex", + "hex-literal", + "serde", + "serde-utils", + "serde_json", + "thiserror", + "unionlabs", +] + [[package]] name = "linux-raw-sys" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index fe485fb7c9..ce4ff28b88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,10 @@ members = [ "lib/cometbls-groth16-verifier", "lib/ethereum-verifier", "lib/gnark-key-parser", + "lib/gnark-mimc", "lib/ics-008-wasm-client", "lib/ics23", + "lib/linea-verifier", "lib/macros", "lib/pg-queue", "lib/poseidon-rs", @@ -45,12 +47,12 @@ members = [ "lib/unionlabs", "lib/voyager-message", "lib/zktrie-rs", - "lib/gnark-key-parser", "light-clients/cometbls-light-client", "light-clients/ethereum-light-client", "light-clients/scroll-light-client", "light-clients/tendermint-light-client", + "light-clients/linea-light-client", "tools/generate-rust-sol-bindings", "tools/keygen", @@ -61,6 +63,7 @@ members = [ "unionvisor", "voyager", "zerg", + "lib/linea-zktrie", ] [workspace.package] @@ -83,8 +86,11 @@ cometbls-groth16-verifier = { path = "lib/cometbls-groth16-verifier", default-fe contracts = { path = "generated/rust/contracts", default-features = false } ethereum-verifier = { path = "lib/ethereum-verifier", default-features = false } gnark-key-parser = { path = "lib/gnark-key-parser", default-features = false } +gnark-mimc = { path = "lib/gnark-mimc", default-features = false } ics008-wasm-client = { path = "lib/ics-008-wasm-client", default-features = false } ics23 = { path = "lib/ics23", default-features = false } +linea-verifier = { path = "lib/linea-verifier", default-features = false } +linea-zktrie = { path = "lib/linea-zktrie", default-features = false } macros = { path = "lib/macros", default-features = false } pg-queue = { path = "lib/pg-queue", default-features = false } poseidon-rs = { path = "lib/poseidon-rs", default-features = false } diff --git a/generated/rust/protos/Cargo.toml b/generated/rust/protos/Cargo.toml index 02234f2e75..383d9d4d78 100644 --- a/generated/rust/protos/Cargo.toml +++ b/generated/rust/protos/Cargo.toml @@ -272,6 +272,7 @@ proto_full = [ "union+galois+api+v3", "union+ibc+lightclients+cometbls+v1", "union+ibc+lightclients+ethereum+v1", + "union+ibc+lightclients+linea+v1", "union+ibc+lightclients+scroll+v1", "union+ics23+v1", "union+staking+v1", @@ -305,6 +306,7 @@ proto_full = [ "ibc+core+commitment+v1", ] "union+ibc+lightclients+ethereum+v1" = ["ibc+core+client+v1", "ibc+lightclients+tendermint+v1"] +"union+ibc+lightclients+linea+v1" = ["ibc+core+client+v1", "union+ibc+lightclients+ethereum+v1"] "union+ibc+lightclients+scroll+v1" = ["ibc+core+client+v1", "union+ibc+lightclients+ethereum+v1"] "union+ics23+v1" = [] "union+staking+v1" = ["cosmos+staking+v1beta1"] diff --git a/generated/rust/protos/src/lib.rs b/generated/rust/protos/src/lib.rs index 820ef69e53..65c4a61e70 100644 --- a/generated/rust/protos/src/lib.rs +++ b/generated/rust/protos/src/lib.rs @@ -859,6 +859,14 @@ pub mod union { // @@protoc_insertion_point(union.ibc.lightclients.ethereum.v1) } } + pub mod linea { + #[cfg(feature = "union+ibc+lightclients+linea+v1")] + // @@protoc_insertion_point(attribute:union.ibc.lightclients.linea.v1) + pub mod v1 { + include!("union.ibc.lightclients.linea.v1.rs"); + // @@protoc_insertion_point(union.ibc.lightclients.linea.v1) + } + } pub mod scroll { #[cfg(feature = "union+ibc+lightclients+scroll+v1")] // @@protoc_insertion_point(attribute:union.ibc.lightclients.scroll.v1) diff --git a/generated/rust/protos/src/union.ibc.lightclients.linea.v1.rs b/generated/rust/protos/src/union.ibc.lightclients.linea.v1.rs new file mode 100644 index 0000000000..e062557c98 --- /dev/null +++ b/generated/rust/protos/src/union.ibc.lightclients.linea.v1.rs @@ -0,0 +1,157 @@ +// @generated +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ClientState { + #[prost(string, tag = "1")] + pub chain_id: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub l1_latest_height: + ::core::option::Option, + #[prost(string, tag = "3")] + pub l1_client_id: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "4")] + pub l1_rollup_contract_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "5")] + pub l1_rollup_current_l2_block_number_slot: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "6")] + pub l1_rollup_current_l2_timestamp_slot: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "7")] + pub l1_rollup_l2_state_root_hashes_slot: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "8")] + pub l2_ibc_contract_address: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "9")] + pub l2_ibc_contract_commitment_slot: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "10")] + pub frozen_height: + ::core::option::Option, +} +impl ::prost::Name for ClientState { + const NAME: &'static str = "ClientState"; + const PACKAGE: &'static str = "union.ibc.lightclients.linea.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("union.ibc.lightclients.linea.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConsensusState { + #[prost(bytes = "vec", tag = "1")] + pub ibc_storage_root: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "2")] + pub timestamp: u64, +} +impl ::prost::Name for ConsensusState { + const NAME: &'static str = "ConsensusState"; + const PACKAGE: &'static str = "union.ibc.lightclients.linea.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("union.ibc.lightclients.linea.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Header { + #[prost(message, optional, tag = "1")] + pub l1_height: + ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub l1_rollup_contract_proof: ::core::option::Option, + #[prost(uint64, tag = "3")] + pub l2_block_number: u64, + #[prost(message, optional, tag = "4")] + pub l2_block_number_proof: ::core::option::Option, + #[prost(bytes = "vec", tag = "5")] + pub l2_state_root: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "6")] + pub l2_state_root_proof: ::core::option::Option, + #[prost(uint64, tag = "7")] + pub l2_timestamp: u64, + #[prost(message, optional, tag = "8")] + pub l2_timestamp_proof: ::core::option::Option, + #[prost(message, optional, tag = "9")] + pub l2_ibc_contract_proof: ::core::option::Option, +} +impl ::prost::Name for Header { + const NAME: &'static str = "Header"; + const PACKAGE: &'static str = "union.ibc.lightclients.linea.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("union.ibc.lightclients.linea.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MerklePath { + #[prost(bytes = "vec", tag = "1")] + pub value: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", repeated, tag = "2")] + pub proof_related_nodes: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +impl ::prost::Name for MerklePath { + const NAME: &'static str = "MerklePath"; + const PACKAGE: &'static str = "union.ibc.lightclients.linea.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("union.ibc.lightclients.linea.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InclusionProof { + #[prost(bytes = "vec", tag = "1")] + pub key: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "2")] + pub leaf_index: u64, + #[prost(message, optional, tag = "3")] + pub merkle_path: ::core::option::Option, +} +impl ::prost::Name for InclusionProof { + const NAME: &'static str = "InclusionProof"; + const PACKAGE: &'static str = "union.ibc.lightclients.linea.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("union.ibc.lightclients.linea.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NonInclusionProof { + #[prost(bytes = "vec", tag = "1")] + pub key: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "2")] + pub left_leaf_index: u64, + #[prost(message, optional, tag = "3")] + pub left_proof: ::core::option::Option, + #[prost(uint64, tag = "4")] + pub right_leaf_index: u64, + #[prost(message, optional, tag = "5")] + pub right_proof: ::core::option::Option, +} +impl ::prost::Name for NonInclusionProof { + const NAME: &'static str = "NonInclusionProof"; + const PACKAGE: &'static str = "union.ibc.lightclients.linea.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("union.ibc.lightclients.linea.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MerkleProof { + #[prost(oneof = "merkle_proof::Proof", tags = "1, 2")] + pub proof: ::core::option::Option, +} +/// Nested message and enum types in `MerkleProof`. +pub mod merkle_proof { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Proof { + #[prost(message, tag = "1")] + Inclusion(super::InclusionProof), + #[prost(message, tag = "2")] + Noninclusion(super::NonInclusionProof), + } +} +impl ::prost::Name for MerkleProof { + const NAME: &'static str = "MerkleProof"; + const PACKAGE: &'static str = "union.ibc.lightclients.linea.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("union.ibc.lightclients.linea.v1.{}", Self::NAME) + } +} +// @@protoc_insertion_point(module) diff --git a/lib/gnark-mimc/Cargo.toml b/lib/gnark-mimc/Cargo.toml new file mode 100644 index 0000000000..c0c4b1fe94 --- /dev/null +++ b/lib/gnark-mimc/Cargo.toml @@ -0,0 +1,24 @@ +[package] +edition.workspace = true +license-file.workspace = true +name = "gnark-mimc" +repository.workspace = true +version = "0.1.0" + +[dependencies] +ark-bls12-377 = { version = "0.4", default-features = false, features = ["curve"] } +ark-bn254 = { version = "0.4", default-features = false, features = ["curve"] } +ark-ff = { version = "0.4", default-features = false } +num-bigint = { workspace = true } +sha3 = { workspace = true } +thiserror = { workspace = true } +tiny-keccak = { workspace = true, features = ["keccak"] } +unionlabs = { workspace = true } + +[dev-dependencies] +serde = { workspace = true } +serde-utils = { workspace = true } +serde_json = { workspace = true } + +[lints] +workspace = true diff --git a/lib/gnark-mimc/src/lib.rs b/lib/gnark-mimc/src/lib.rs new file mode 100644 index 0000000000..2f6ad1d594 --- /dev/null +++ b/lib/gnark-mimc/src/lib.rs @@ -0,0 +1,292 @@ +use std::marker::PhantomData; + +use ark_ff::PrimeField; +use num_bigint::BigUint; +use sha3::Digest; +use unionlabs::{ + errors::{ExpectedLength, InvalidLength}, + hash::H256, +}; + +// https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bn254/fr/mimc/mimc.go#L31 +pub const GNARK_SEED: &[u8] = b"seed"; + +// https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bn254/fr/mimc/mimc.go#L30 +pub const GNARK_BN254_ROUNDS: usize = 110; +// https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bn254/fr/mimc/mimc.go#L158 +pub const GNARK_BN254_E: u64 = 5; + +// https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bls12-377/fr/mimc/mimc.go#L30C17-L30C19 +pub const GNARK_BLS12_377_ROUNDS: usize = 62; +// https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bls12-377/fr/mimc/mimc.go#L158 +pub const GNARK_BLS12_377_E: u64 = 17; + +pub type MiMCBn254Constants = MiMCConstants; +pub type MiMCBn254<'a> = MiMC<'a, ark_bn254::Fr, { GNARK_BN254_ROUNDS }, { GNARK_BN254_E }>; + +pub type MiMCBls12377Constants = MiMCConstants; +pub type MiMCBls12377<'a> = + MiMC<'a, ark_bls12_377::Fr, { GNARK_BLS12_377_ROUNDS }, { GNARK_BLS12_377_E }>; + +pub fn new_mimc_constants_bls12_377() -> MiMCBls12377Constants { + MiMCConstants::new(GNARK_SEED) +} + +pub fn new_mimc_bls12_377(constants: &MiMCBls12377Constants) -> MiMCBls12377 { + MiMC::new(&constants) +} + +pub fn mimc_sum_bl12377( + constants: &MiMCBls12377Constants, + elements: impl AsRef<[u8]>, +) -> Result { + Ok(new_mimc_bls12_377(constants) + .update(elements)? + .finalize() + .try_into() + .expect("impossible")) +} + +pub fn new_mimc_constants_bn254() -> MiMCConstants { + MiMCConstants::new(GNARK_SEED) +} + +pub fn new_mimc_bn254(constants: &MiMCBn254Constants) -> MiMCBn254 { + MiMC::new(&constants) +} + +pub fn mimc_sum_bn254( + constants: &MiMCBn254Constants, + elements: impl AsRef<[u8]>, +) -> Result { + Ok(new_mimc_bn254(constants) + .update(elements)? + .finalize() + .try_into() + .expect("impossible")) +} + +#[derive(Clone, Debug, PartialEq, thiserror::Error)] +pub enum Error { + #[error("invalid length {0}")] + InvalidLength(InvalidLength), + #[error("invalid field element: {value:?}")] + InvalidFieldElement { value: Vec }, +} + +#[derive(Debug, PartialEq)] +pub struct MiMCConstants([F; K]); + +impl AsRef<[F; K]> for MiMCConstants { + fn as_ref(&self) -> &[F; K] { + &self.0 + } +} + +impl MiMCConstants { + // TODO: move this to build.rs as a constant preset + // https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bn254/fr/mimc/mimc.go#L179 + pub fn new(seed: &[u8]) -> Self { + let keccak = |x: &[u8]| sha3::Keccak256::new().chain_update(x).finalize(); + let round_zero = keccak(seed); + let (_, constants) = + (0..K).fold((round_zero, [F::zero(); K]), |(round, mut constants), i| { + let constant = keccak(&round); + constants[i] = F::from_be_bytes_mod_order(&constant); + (constant, constants) + }); + Self(constants) + } +} + +pub struct MiMC<'a, F, const K: usize, const E: u64> { + constants: &'a MiMCConstants, + data: Vec, + _marker: PhantomData, +} + +impl<'a, F: PrimeField, const K: usize, const E: u64> MiMC<'a, F, K, E> { + pub const FIELD_ELEMENT_BYTES_LEN: usize = + (F::MODULUS_BIT_SIZE.next_power_of_two() / 8) as usize; + + pub fn new(constants: &'a MiMCConstants) -> Self { + Self { + _marker: PhantomData, + constants, + data: Vec::default(), + } + } + + // https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bn254/fr/mimc/mimc.go#L105 + pub fn update(mut self, elements: impl AsRef<[u8]>) -> Result { + // Slight difference, we only accept a mutiple of the field. No hidden, implicit padding. + let elements = elements.as_ref(); + if elements.len() % Self::FIELD_ELEMENT_BYTES_LEN != 0 { + return Err(Error::InvalidLength(InvalidLength { + expected: ExpectedLength::Exact(0), + found: elements.len(), + })); + } + let nb_of_field_elements = elements.len() / Self::FIELD_ELEMENT_BYTES_LEN; + for i in 0..nb_of_field_elements { + self.data.push( + F::from_bigint( + BigUint::from_bytes_be( + &elements[i * Self::FIELD_ELEMENT_BYTES_LEN + ..i * Self::FIELD_ELEMENT_BYTES_LEN + Self::FIELD_ELEMENT_BYTES_LEN], + ) + .try_into() + .map_err(|_| Error::InvalidFieldElement { + value: elements.to_vec(), + })?, + ) + .ok_or(Error::InvalidFieldElement { + value: elements.to_vec(), + })?, + ) + } + Ok(self) + } + + // Inlined version of: + // https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bn254/fr/mimc/mimc.go#L169 + pub fn finalize(&self) -> Vec { + let sum = self.data.iter().fold(F::zero(), |k_acc, &data_i| { + // encrypt + let r = self.constants.as_ref().iter().fold(data_i, |m_acc, c_i| { + // (m + k + c) ^ e + let m_k_c = m_acc + k_acc + c_i; + F::pow(&m_k_c, [E]) + }) + k_acc; + k_acc + r + data_i + }); + // Extremely ugly interface from arkworks, not gonna lie + let mut buffer = vec![0u8; Self::FIELD_ELEMENT_BYTES_LEN]; + sum.serialize_uncompressed(&mut buffer[..]) + .expect("impossible"); + // No way to provide endianness when serializing, ark is little, gnark is big + buffer.reverse(); + buffer + } + + pub fn reset(&mut self) { + self.data.clear(); + } +} + +#[cfg(test)] +mod tests { + use serde::Deserialize; + + use crate::{MiMC, MiMCConstants, GNARK_BN254_E, GNARK_BN254_ROUNDS, GNARK_SEED}; + + #[derive(Deserialize)] + struct Test { + #[serde(rename = "in")] + #[serde(with = "::serde_utils::hex_string_list")] + i: Vec>, + #[serde(rename = "out")] + #[serde(with = "::serde_utils::hex_string")] + o: Vec, + } + + // The hex strings have been padded to even number of characters + // https://github.com/Consensys/gnark-crypto/blob/564b6f724c3beac52d805e6e600d0a1fda9770b5/ecc/bn254/fr/mimc/test_vectors/vectors.json + #[test] + fn test_bn254_gnark_vectors() { + let tests_vector = r#" + [ + { + "in": [ + "0x105afe02a0f7648bee1669b05bf7ae69a37dbb6c86ebbee325dffe97ac1f8e64" + ], + "out": "0x263b9e754e6c611d646e65b16c48f51ab7bc0abedfae9c6ea04e2814ed28daf4" + }, + { + "in": [ + "0x00bc35f0589078e34d9139a357175d0e74b843e3de2f56bb6a5f18032ff3f627" + ], + "out": "0x103e2c8f50dec5248fd68f9778429252364ff239b123977a697356082f524f25" + }, + { + "in": [ + "0x208f0b283064057cf912b65eaa51e2cb2b85fdbe2fd0b2841f4bca59321ef1bf", + "0x226bee7671296d05c998a5b5b4b1d25f478696d5997ba4f4be1a682c56a69e11" + ], + "out": "0x1476ada1433d73817a69e45c84c5d452ad858f2dfdb1f7e4da203d3c4fd42222" + }, + { + "in": [ + "0x00995d448ab1fc86dd4874ebcbc0a7eea41acbe2c76e300aa73a1a0e63d5bc1b", + "0x2190a93f59d9f8cbb4f6236c5b7bf511aec80e88bec71dad4f5bbba9346ff5e4" + ], + "out": "0x0cd4ef5556a9413b6bb98d12aba6ed9b937f0adce41ba618a212fdcb1629737a" + }, + { + "in": [ + "0x06680de43f6cf410d4a8ed2893e58a8b740bac14f9dbdadbc8623c06027418a1", + "0x059323b0ab7043f559674eba263da812eae9e933b0c1bad55f8118d0caaa7479", + "0x16b161c8de7184ccc6b1b6fcddb562789a68eeaec174376f1157dfb3db310787" + ], + "out": "0x118e5255aabe7a3b6a5dde6ca28de461d36f802653885c665745fc4e6ca0f709" + }, + { + "in": [ + "0x1ab45102976d9ec683b46e7e7b4163055d1ab768d6bbd56cf95f3bca15d58020", + "0x18ff125903dc8352ca63c7a436f0425b4b7ddf7e487fb9ffd30f151993571b57", + "0x2cbfaa412f4b612d611acaab79a9e1c06b7094d8754fdbc085db28f2e4dd09ab" + ], + "out": "0x025fa55a9896d91d9617d9512e061d754336816f748bf07566591ec5cf4680dd" + }, + { + "in": [ + "0x2eddc35df3778e61c6571bcad90ab41dbf3cb61f4fd203d1922eb4fafde99136", + "0x0905c2010ece23e26373b38b6fc8b3c932a59443af656fb164e22b2bcf940b5a", + "0x22e63a3eb565d13c42c7d520c7b6112534b1c666653452f743b80bcc2d878455", + "0x096dff377f354f792685a7e740e3024409c24a379425ff63e3ce320b1e9bc471" + ], + "out": "0x18ea2fd58b4f3274193f3e73bded685426f114a8e7bc373c1aee3e6f0125787b" + }, + { + "in": [ + "0x05f3e89a9418877cd586de7c5cb061e6701a1bd69074cc7bd97c7c39d8f955eb", + "0x20cdf81f33b895b442d47357bd80e1eca03f410d808324f6d151dc68ab354a1f", + "0x12f4c27e5a2e80dd67fb33928c4e6219a8bdc89b498ed32acb02d725cec90076", + "0x1d6b52c237f0f74f0c50755627eed2610608488b54b0a3941a4623b1d435232a" + ], + "out": "0x2f237dea4570779296e2866383740b8e9ccf59577f8ff729880dadb58ae34d47" + }, + { + "in": [ + "0x262c77f7fdef59c80e0a9d4ece6d18fb6d64ebaacfc21921f44c5adc19698c6a", + "0x087bb7a78b27d19c5a502fbb087e48785d2777cff15d7b493901a8e528b64ee0", + "0x2a8a0e2a793fdd5bc340857b355f2b4c00c2723cefdf8515bda5beef458fca2b", + "0x2d4232cb721888f71997377de5ca195a5ae03d3eb9c87d2c04ef3664759036da", + "0x2f623ee75518430e291d42e7aaa75f5291a1bbfed125426d39270046a26be35a" + ], + "out": "0x06246dee7e2d9560a074c50a06e6525e4a58395cea4a893c49d71e373f19b9d6" + }, + { + "in": [ + "0x14b09f9af90cafa8a4e508f5289a6868804f98d3a724162999193e6c4bf752ea", + "0x0727359808271f360a6136389a9e2d5b1bb6ff3e8c4125ca03005892446ac17d", + "0x2b4abbd9943b201c1f75754833684f9eb15728a2ba646c53c2614bea7c9b968b", + "0x08e0ddb80366c4c6c7dcb9090f4862d64ef40677d324a76a82e06ca33ad29a09", + "0x170e8c954ca7e6526b743e92f796488afe5083a9c549358f730659c3e1cdbafa" + ], + "out": "0x1a2e7cffb5183898a8f4f6d4699bc272665ebffbb9d095576d2e21c45f012358" + } + ] + "#; + let tests = serde_json::from_str::>(tests_vector).unwrap(); + let constants = MiMCConstants::::new(GNARK_SEED); + for test in tests { + let input = test.i.into_iter().flatten().collect::>(); + let sum = MiMC::<_, { GNARK_BN254_ROUNDS }, { GNARK_BN254_E }>::new(&constants) + .update(&input) + .unwrap() + .finalize(); + assert_eq!(sum, test.o) + } + } +} diff --git a/lib/linea-verifier/Cargo.toml b/lib/linea-verifier/Cargo.toml new file mode 100644 index 0000000000..cdd13b27d1 --- /dev/null +++ b/lib/linea-verifier/Cargo.toml @@ -0,0 +1,28 @@ +[package] +edition = { workspace = true } +license-file = { workspace = true } +name = "linea-verifier" +repository = { workspace = true } +version = "0.1.0" + +[lints] +workspace = true + +[package.metadata.crane] +test-include = ["lib/scroll-verifier/tests"] + +[dependencies] +ethereum-verifier = { workspace = true } +ethers-core.workspace = true +gnark-mimc = { workspace = true } +hex = { workspace = true } +hex-literal.workspace = true +linea-zktrie = { workspace = true } +rlp = { workspace = true } +scroll-codec.workspace = true +serde = { workspace = true } +serde-utils = { workspace = true } +serde_json = { workspace = true } +sha3 = { workspace = true } +thiserror = { workspace = true } +unionlabs = { workspace = true } diff --git a/lib/linea-verifier/linea-verifier.nix b/lib/linea-verifier/linea-verifier.nix new file mode 100644 index 0000000000..cd5b12a120 --- /dev/null +++ b/lib/linea-verifier/linea-verifier.nix @@ -0,0 +1,11 @@ +{ ... }: { + perSystem = { self', pkgs, system, config, crane, stdenv, dbg, lib, ... }: + let + scroll-verifier-all = (crane.buildWorkspaceMember { + crateDirFromRoot = "lib/linea-verifier"; + }); + in + { + inherit (scroll-verifier-all) checks; + }; +} diff --git a/lib/linea-verifier/src/lib.rs b/lib/linea-verifier/src/lib.rs new file mode 100644 index 0000000000..aa376f796c --- /dev/null +++ b/lib/linea-verifier/src/lib.rs @@ -0,0 +1,2 @@ +pub mod verify; +pub use verify::*; diff --git a/lib/linea-verifier/src/verify.rs b/lib/linea-verifier/src/verify.rs new file mode 100644 index 0000000000..c5be5e2fb3 --- /dev/null +++ b/lib/linea-verifier/src/verify.rs @@ -0,0 +1,106 @@ +use core::fmt::Debug; + +use ethereum_verifier::{verify_account_storage_root, verify_storage_proof}; +use gnark_mimc::new_mimc_constants_bls12_377; +use sha3::Digest; +use unionlabs::{ + hash::H256, + ibc::lightclients::linea::{client_state::ClientState, header::Header}, + linea::account::ZkAccount, + uint::U256, +}; + +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum Error { + #[error("invalid rollup contract proof {0}")] + InvalidRollupContractProof(ethereum_verifier::Error), + #[error("invalid l2 block number proof {0}")] + InvalidL2BlockNumberProof(ethereum_verifier::Error), + #[error("invalid l2 timestamp proof {0}")] + InvalidL2TimestampProof(ethereum_verifier::Error), + #[error("invalid l2 state root {0}")] + InvalidL2StateRootProof(ethereum_verifier::Error), + #[error("invalid l2 ibc contract proof {0}")] + InvalidL2IbcContractProof(linea_zktrie::verify::Error), + #[error("the l2 ibc contract proof must be an inclusion proof, verify the address?")] + L2IbcContractProofIsNotInclusion, + #[error("node value mismatch")] + ValueMismatch, +} + +/* +1. assert rootHash(rollup) in l1StateRoot +2. assert rollup.currentL2BlockNumber = l2BlockNumber +3. assert rollup.currentL2Timestamp = l2Timestamp +4. assert rollup.stateRootHashes[l2BlockNumber] = l2StateRoot +5. assert rootHash(l2IbcContract) in l2StateRoot + */ +pub fn verify_header( + client_state: ClientState, + header: Header, + l1_state_root: H256, +) -> Result<(), Error> { + // 1. + verify_account_storage_root( + l1_state_root, + &client_state.l1_rollup_contract_address, + &header.l1_rollup_contract_proof.proof, + &header.l1_rollup_contract_proof.storage_root, + ) + .map_err(Error::InvalidRollupContractProof)?; + + // 2. + verify_storage_proof( + header.l1_rollup_contract_proof.storage_root, + client_state.l1_rollup_current_l2_block_number_slot, + &rlp::encode(&header.l2_block_number), + &header.l2_block_number_proof.proofs[0].proof, + ) + .map_err(Error::InvalidL2BlockNumberProof)?; + + // 3. + verify_storage_proof( + header.l1_rollup_contract_proof.storage_root, + client_state.l1_rollup_current_l2_timestamp_slot, + &rlp::encode(&header.l2_timestamp), + &header.l2_timestamp_proof.proofs[0].proof, + ) + .map_err(Error::InvalidL2TimestampProof)?; + + // 4. + verify_storage_proof( + header.l1_rollup_contract_proof.storage_root, + state_root_hashes_mapping_key( + client_state.l1_rollup_l2_state_root_hashes_slot, + header.l2_block_number.into(), + ), + &rlp::encode(&U256::from_be_bytes(header.l2_state_root.into())), + &header.l2_state_root_proof.proofs[0].proof, + ) + .map_err(Error::InvalidL2StateRootProof)?; + + // 5. + // TODO: perhaps force the proof to be an actual inclusion proof off-chain + let account = linea_zktrie::verify::verify::( + &new_mimc_constants_bls12_377(), + &header.l2_ibc_contract_proof, + header.l2_state_root, + client_state.l2_ibc_contract_address, + ) + .map_err(Error::InvalidL2IbcContractProof)?; + + match account { + Some(_) => Ok(()), + None => Err(Error::L2IbcContractProofIsNotInclusion), + } +} + +pub fn state_root_hashes_mapping_key(slot: U256, l2_block_number: U256) -> U256 { + U256::from_be_bytes( + sha3::Keccak256::new() + .chain_update(l2_block_number.to_be_bytes()) + .chain_update(slot.to_be_bytes()) + .finalize() + .into(), + ) +} diff --git a/lib/linea-verifier/tests/scroll_absent.json b/lib/linea-verifier/tests/scroll_absent.json new file mode 100644 index 0000000000..dfb98aa2ba --- /dev/null +++ b/lib/linea-verifier/tests/scroll_absent.json @@ -0,0 +1,10 @@ +{ + "key": "0xFF", + "value": "0x0", + "proof": [ + "0x092ae559c4a5791aa624938167828ea4509d88eaa82114504464c72cbd682e1fd1061c6d68c9639dab7cf8bfb78aadeca93a9bab93dbed21a2c26c92b8877a99e9", + "0x080b57786fb3f84de0a36e57cb2c13baae5ccffd43be3f75c5590d473128811fc40000000000000000000000000000000000000000000000000000000000000000", + "0x05", + "0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449" + ] +} diff --git a/lib/linea-verifier/tests/scroll_header.json b/lib/linea-verifier/tests/scroll_header.json new file mode 100644 index 0000000000..7f1cab34c1 --- /dev/null +++ b/lib/linea-verifier/tests/scroll_header.json @@ -0,0 +1,121 @@ +{ + "l1_height": { + "revision_height": 4386369, + "revision_number": 0 + }, + "l2_state_root": "0x2e5537b891c2cf0ba5e6b3bb68d2d06772c305a551e7fa8a18101ae1b2e4dc02", + "l2_state_proof": { + "proofs": [ + { + "key": "0x719fcdd8a682322fec50b4ca48958cd082f181fe38e628e44a6e6745e213b2c", + "proof": [ + "0xf90211a000b115159e2cf274be4a480850f31ade5f2d5c8250e9df5cd5bc7ea60781dfc9a00c20f366dd9f8a1bd3aa464331c27776df116f49249e97e8a07bdc63b576c20da0731bdb9e0e63d8822eefee6a128fa6d08a1ef785634afa846be81702ef39ba0da0a06590528dd0e27f43682ed353ab3efb997b1e38cb72a5449fcbf9f14927a92aa08becca8e365d7e2ceee9ae7b1f0c4a66303ea3ec8a1bed63cfa48daf0f885ed3a0e05b013911fd1834327cb92e4f7e74bb38ff6f92d0ad0d1d87695b69ebf62c70a0ae6412f3d352a6cdfc06fefbbcd9845bb9b3422c205ddbc8ecd90f18207abdf4a001ffce658c9b2a5b32797f611fafaa21c107dd88cc43aebbd1b8be318f34d10ba0145cddbde2805661d5fc9d8bfac4a3168e9ce299d84c29df85833a5b8a8b0d4fa0a47ce92df9913f3ed8e7a178fb6f8ced040b8c1dcb1076b8280301a50a329d52a07de1c1f78a7dfb5aa2b84e09a837e381b1517d14aa15468b8297977ccb3f04a1a0cc4ed37c770000e3748487aa32ccba9112dc5997884868e5228f200aa362bb56a00ecaeb6cdd27af319aeaf3445b3f771582ceb20d41c480d60f0560712f674dffa016a1a71f7227fb0bdc22f531539053c4136335512e833b9a8a7a0f713f26957ba097c8879c188a6b63a542e61705dc3048a6403b33ed4d2b8f1f873010ce58a61ca007fc91a96001e1f19cf482aa39fc646ceead15370bb6f8d0a9277c47adbf36d680", + "0xf90211a0cdf44ce9e25b57efcc0fe07d7d652ff20153762e5754914bcd48083c4a395cc2a01542071a30263719004733ba2d6885d4bbb1efd594879b2323ec2c3dec2f9649a05ae8e548a9a2e636f049c7108e4250403f799cef4acccfbdc1832cdd6c1e2cd0a02c1d5f59377779e2f655209ba3aedcdf8b1ec2f0f417f196bb3676fc75b2faf8a059e767c07c90c041572c71696b0f52161c43327565656962b7164ae4cbe2da3ea0fd3ddbd5801bdf17d3263d95f6ed79dfb8000048e5c1ea5eb7259a1361b8f940a0e49594ed4b172a96199eaa7a97d1c205db39497cc2f3da01a668330fb84847f2a09a95d518175125866172483a1d4c36c1449314de28dd108b515f9345240d35f1a0782b81d7c9fb297e9d10af1154793ddea98ecf38c0b8292a38cb9ec6694ccdc9a0d5826da599421b90f9e8812d3d867f6c1c693b67771451e215a1822a787f3fb2a0e5650505d2b09c1672a712a17980149582069f54e6100a35aa8dcc4c117b4d0fa05d4f8fe2dfe435b656912fa26a12ef3e870bcedf885e00cd12c5f1a181c481a5a057530a18fac63250a7340befb8dbf402a4ef468072c77e2b40cf065373e5ea22a0c57063ebcdef9d474f11a92461656346fbf3cd6dd03bd03d1d67d788d995fa26a03873d47eefb42c88c9879f388c6dc6ebe495ec439655802ede98e37b0c485326a09e6edd2b9247321aece59161b6d10bb6841971503e9b193e874acfc574dcfd5d80", + "0xf90211a01133306c27f8779185dd8a0098e515cd52779fe9d8d507b5b39fe7ab6516d32ca0e7300a240c5f367a4a7a7af6ef0374a59a41d37d35f98590d4cd1b68b3f87444a043ebc5da4d44f29f5ee19a4dd5a7972559ea5fe3b4fad6caddcbd3541ebf84c8a08baa52401708a0e2f6fc4bc25a9a50cc040926e0eb5e57f48d8d0043e1a6fd4fa0c57970982de27cc3568ca06534ce1055542f8f6999023a52e5141213c22442e8a032bf1ad6a8a0b8f1194a3cdcd37acb3cfe37e74abea7eb19fe41ff05f9c2f114a0cda8f6b2fb8ea4ba0dcb711d49c557b16f3ac60401b2e493f5ef0b17e27801f4a05d48c378a1f9e1e5fa2a356dd8304ef970f393cba4752dc1dce2647aea02a032a0dbea2593fd402d5fba571564aabe85e58d3133fd129b2089dbd00ec07c895e83a0cdae72850b9246763fe5039ea1d2974fdbfc75956f33d1a4e23a8b713fc69420a0c358206bc9fbf8e21b4c1bd05d53c2400c9d107d7717709ae7b2dcf1d9c1e4bea0d96d89e7d61de54e7a5ce0f9fcd3825ab2e958b9db8699363db86467d0f69e7ca081d36e6696cdd65c5bcb14e5ffa09ae39b09dcaa124c6a43ade794c27b7346aea072526e6291d63a2770c3b488868f47381a8476c39b2ed2e9b8ce8d0811ca6a1ca027b546dd194714f17cb3bce031335f3349a26cda1d230acbcec72e32236c6732a06c54bb1fd38b84f06ca77ac74009de0af97870034ea62a86f0a777deb4044ea380", + "0xf901f1a09f8d14a8e464a56ad3cda275041ab176627056f894624024980b403ec4e6af38a028dbcb849747e367e74572292b40b6b36e3e58795d57016e7d47514b98ec88d980a0a0032d9fc56d89669a7fec5d071843014c57c8fe5a8519544458d7a29d268f16a0ea955d50b165478250e584d73bbd2f60ac9241ae76215704bac9d9bc36f762faa04f57706a6615022444ea857dfd048769950a5d20ab86d89f26ba162b951c0e22a0bd3653950d087e95610e0d3a80052c3e7e28542cd1925cc192362a7f22b00689a01b4bd402fd9a327e870b59fad0f3d96a208a0942545a7c9cf9d50c74e19a12dba04adefbfdfcbc26c2321d7a2f73c22d7ffaebde5f074d9cecc06c538c204e0b10a01484f4bffba3fa89001d7ef94fae025a7a3ef9fe08ce88940b1910dd96ebded8a010790de53fb866b5159d48bf7ee8f31728c33562bd3f2e6ef5a8fe06975c033ca07b8facf914520a7e30b4f84b1eafa01e3d4de71bf827f45f2f964606dc1f094fa0bfb7e96e8eb644d254afae1c066b3be65e7a562ab3c51fc3bf119dacac593eb7a003c8838fbae9541056166d9a6fc711875bbd9a2348d3de30575cc86e552d922da08c70f5f7faf0008b04ba5d9480a0929a57780f991e7768923cf7ffe0a5a5191fa0721933d85a4ab5d6180c39660cd8dfc0fd70f7005b9f9b20c465899033df829a80", + "0xf891808080a0a49a65d5c8a51554369dc1c33a129eb4a212616a560b230bb2f3f0ddc34296218080a0f73ac2e3f05cc2e334e04435ce0bacf490bcc54e327583f2312d62b4202a7d6f808080a0c217f8d7ae00d490c94f0dde5b7221bda2efd932e8d5b4fbe40a7f0e6efe33a5808080a063315ac8338e3fe65df494f959d51bf46716bdd3841fd5687b43dbbde3cba7de8080", + "0xf8419e388bb7b570bfb9d27903a47171f6c3f9f6f01df97efcd02cb816e541288aa1a02e5537b891c2cf0ba5e6b3bb68d2d06772c305a551e7fa8a18101ae1b2e4dc02" + ], + "value": "0x2e5537b891c2cf0ba5e6b3bb68d2d06772c305a551e7fa8a18101ae1b2e4dc02" + } + ] + }, + "batch_hash_proof": { + "proofs": [ + { + "key": "0x6f03ba64d87195e325715c9846905fdca8bd692f7dab65dedac7ee82c043ece3", + "proof": [ + "0xf90211a000b115159e2cf274be4a480850f31ade5f2d5c8250e9df5cd5bc7ea60781dfc9a00c20f366dd9f8a1bd3aa464331c27776df116f49249e97e8a07bdc63b576c20da0731bdb9e0e63d8822eefee6a128fa6d08a1ef785634afa846be81702ef39ba0da0a06590528dd0e27f43682ed353ab3efb997b1e38cb72a5449fcbf9f14927a92aa08becca8e365d7e2ceee9ae7b1f0c4a66303ea3ec8a1bed63cfa48daf0f885ed3a0e05b013911fd1834327cb92e4f7e74bb38ff6f92d0ad0d1d87695b69ebf62c70a0ae6412f3d352a6cdfc06fefbbcd9845bb9b3422c205ddbc8ecd90f18207abdf4a001ffce658c9b2a5b32797f611fafaa21c107dd88cc43aebbd1b8be318f34d10ba0145cddbde2805661d5fc9d8bfac4a3168e9ce299d84c29df85833a5b8a8b0d4fa0a47ce92df9913f3ed8e7a178fb6f8ced040b8c1dcb1076b8280301a50a329d52a07de1c1f78a7dfb5aa2b84e09a837e381b1517d14aa15468b8297977ccb3f04a1a0cc4ed37c770000e3748487aa32ccba9112dc5997884868e5228f200aa362bb56a00ecaeb6cdd27af319aeaf3445b3f771582ceb20d41c480d60f0560712f674dffa016a1a71f7227fb0bdc22f531539053c4136335512e833b9a8a7a0f713f26957ba097c8879c188a6b63a542e61705dc3048a6403b33ed4d2b8f1f873010ce58a61ca007fc91a96001e1f19cf482aa39fc646ceead15370bb6f8d0a9277c47adbf36d680", + "0xf90211a00202ddd87e3fb5ce982c2f17b48e48e0864929c0b1bd99eb15246ab3e5a15270a05c2179709992f028908e67af89a765df5418c11a17656e7e021b473b59d4d664a0257912e3c07831f0fb2a9795e2dd709ad3f4b65e500f3fdcee1ff4ecc95adf4ca0b0aba4db08f14d60f87e71398ddbc93ed4f0f00796b05d5793ec856f38da5101a071ac86650b74ec90c790fdf5010bcb7eaca487daa22e5ab01ea880e4367b8eb1a0d548666078e3edd17dacc2f20c59a4bc1692e01622433197f2b584e2cdd18f78a009aa0ad51facaf289a3722aa28fbf2a8fb91dc99f417bf105b2b3dbb9d2f8e48a0a06846c7ad40355cc296f8855a4d96180554c72d7dd4dfbfda874b1f65ca5804a0b0f42c2a8eb5a8ebcba2309e9f9a929159b8495cee9a1dd79df7eb63f00d3ea9a0fe4d0afe2d03c6c00b824e989ab83e6eb7dab8db0c599890e6e9ab20b5655ed2a006d2349295fe5276f11e7a2c30e2237d1f4769a1ad3616fe43f50d4517bc4155a043c6b0edd45522eb56a4eb56e7700600f3da365625da50cce8dbe954b45fee2ba0872df422fd3e9a960e67c3afdfc058fc1f9c0078e2349df48eb0d4fed3647504a06141205922aa8b35732b3c65f37f1f060af0de3665e3ea2196076f5e73eabecfa02434bbed7ae51ccf3295c7a0ebccb18037aae150aacdb6c5be897fb7e95ce810a0ece5363a834ad1c3b5d2370993f7bfb941593fc95de7dc38281e400e5a42d28080", + "0xf90211a0f25ce434479388a204f0b5abe67252acd3aa64ea4beee08d63f3f37303727b67a061127ff722a404ca1fd51e1a0704ad374083642d1756c0b6b38fa8a92a43db9aa0fe2fe7ebafc6a8374723dc8caa69ca4577033d263cdf8d9d4f1eb644fd787beea03e6620218789bbf1146b077cc969256c8310c5e41d6fcb6f21b27a89f2a7c240a0206d3568398ac8fae115f9eca93ed76b5f1e529079009cea35d9d28eb523df46a0e56d7942de6de772ce7e219ebcd48e7727c52d4bfcf113e3234ff5b224f00994a0c4d372b0e6f240b1dd399796934d776512b327cfe951ca31f3cf1a042c98b177a01af0cf9e2d38f116024bf2c4738f17477dc46ee5cf983565bf67ec89d5e873f9a0a04de6e7db567807ab4e8be4450b936e0db9ffecec8c7424ed06623c2a12ee4fa010adaa092f334bee19c9cde409576f3180c1fd438166434b5bedb53be675049ba0daef256e959a64c56e44f0ab0f23a0be0487feb8b6f80d0eabaf620e062a081ba0f77fd042c8c15052c15772c7161a493b767b9a959d9e4c0cdf440b0d7bfeaac5a0576ab53cea58c7b316b9959418c3af05b7ee595dfba595f674dc6ccd8a47f70aa0b1eea262375da07ea6e2df0b0db39dd8b541044907801a42ba03324b3f0b96ada093a65109f439f65d41cc02db79c52d30f6fd474712aa1a88b72a4567dba255e2a0134b40afc8dca88c65a60234db239794372ece0e98897cf4776afefa7fd3ee7180", + "0xf90211a06b0eca5c480b2ad7215c86c185747052f2415f70918737ee02f6aefd6045072ca04a7782f6cc1471627e5b784f67a7ac76b0ff482970c8270987c2041565b7bc0da04b94ab2b1eb36a61fdbe91f1ab8a6818da0892bd63d1519e189b77e12e517e46a0ee4585709035a3cbd036955416bbada3fe179424b5b68e0d70f1b16a21ac3664a0ea1516c02dd35dea930ca80c79d6bb22676e26979c32d2b00e5c9b31227b6beca0492ab8b8f7f23d1b6a60a0e7d73a34a0012458cf9d74d6cba0a6e972e6be34c3a0113147accd7085315e099ea3318f11fa5e6b46bf321477d2bcc9894add80887da0e2ce70a1df7c9e7c7b01e0df5a8fb8cfe3529ceed8662aed805d8dcd7cf4e888a020bb834c12e3626f9977b468417ff7ec2c1c1ddc3b214d2125b243dcbfd5c838a01576b390b70e4ec4d3deaf37ea741d8a6055acd546aa9496dcf055f2a8bf1a57a00fa96d301ef714ff6bdd3f6fbe828172e431a66682bb07815551d56a2baa8679a06a6246fd818565ec408ac11a7b0acd8e98a2be0932d1651c6ef884bfd6edf6a8a0d28cc13e0dc17936cd7b78e363c2c954ff27a290b559a9cd635754207dc2bbb9a07942afb5e966380f264698e78ce2ab5e96766c6f70ee72a18d738039edd7ee57a03929da27f37e90accda6d2d44bbca5e044355f0dda8c876363cfac27d7e3f91da067e6da428fae3f766e5b643634136e8492bbd24471ab4827edf1a2735526926f80", + "0xf85180808080a0e575d2efc468fbeff8b21453a9506aa62eeabb6f36cc529e1ca81d7821e4531c8080808080808080a0b9ccb2fc5ffb8b9b1170d65cef61ca8626e139a1b8ece06bf3601f66ee4f8598808080", + "0xf8419e32c5b748596942f65ff0f485e4fbafa697f8d2ba0311d893c5a6c2a406cfa1a0c643856a01a6274de2e6895e975c0c129761ab3dfa291cf9733a66eee09318bf" + ], + "value": "0xc643856a01a6274de2e6895e975c0c129761ab3dfa291cf9733a66eee09318bf" + } + ] + }, + "l1_account_proof": { + "proof": [ + "0xf90211a0e3104403338d00c2d30efdf5f294dd8e667f748d9c6d1683bab0d79d0746ce60a024cc94676baa155d81f55fc942d6490da00d273b39fff988c1884c8705444cf4a09cf0b0d7e85bab7d5c59b7e9bf8fba716b5bec1fe75a715e5e59fc23832cef72a0cbc9c9a112f8c1b9b5d7fd976d51b10189261712154c1b65a1148b47efae9ddda08b0fbd0de8cd30588f0d81d39413d88f9f29bcb55b453162313bf1305f022eb4a0f251310de48efccf330e3250ad2318355425c3e6f62d6ae379472e63e83e9b58a0ff38b3f9eb65c59f7b78effa3e3f9979b643516847941a60d05127ee216191d7a04972e3ea37e5c2360b31ee2cac35460b343d711b700d30540bc2ea50f0b6fbf6a0836b4ace2c4d56ed2c506adfa23480d102d62c1175e8a43bc33cea42222a86a7a01fc70af24ae7d5ae7ac0b9dbb165e49f5753e8f7e62a108230d3702dbe37306ea08e1a64dae8f230296978d2b7dd8d0b586e21c0c50bf3111d981485885a631619a046682191a9afd4c94ecc475003db74546ad885edfef501705e280071d5980148a05057ccf385db7099ab7dcee5f81abb24e4c2306453c00cbf8dff7f2025e5747da05e9faabed07d8bd51ab7b9164aed1a7a05ef938e1b1d85a5b325c3503ba6ad0fa0455cfdfc6fb44bdeb6d7909ff4f9bb616afbc0d17e7df717e0d8bac1892bd482a0f6e454796a3e94f5a5f768bbffe1b5406b25e7b483cb2c5517a545fcf2d70ab080", + "0xf90211a0a9f7056b96b1e601d593a8b0ae3a49b539be3f5181b9a5a1b2de1fdad32b55e5a0d3764a4003c4cbbe6bf106efda9c865740d655237e97787c5255e637c25abeefa0d65ed7694abf939d2eed5496efeb5a90d1315edaa7097508227bd7229c43d804a0285fe51776f5717ce83ca64a7f86ada569dfa9525a644e42ed8b511648c85b9ca0cd3c14b051cb7b820b71acca1217046595f83a362a78ea15678e3e9bdc232f43a07294e56a52bbd8229f9ea7abadcb953bced4d06fa4bfa0ddc1c9a31f3fce7582a02d843b54917be4fbe8994485fdae8b54a1cecc200bae7c63e51f1268bdcea4aea0326392be59852ad9f1d3f147b50a1cde58a32f45991d4aafa6eb8d484380e3dda0d967741181e7ce8267ac60217e28968395e183ae5de0cb235c2eac651d26b17ea09b4f7a989916d4606b0a00cccc45f9be734e2203b43cf6d66280cd7287ab3d71a0c05ac1dba791abc6c26b70479fa4c50a864e395ec885bd99691897fea4fc69d5a0ce6a3acf61d9abf06d930758ab66b7343f343395783d5e382017a1e4da4d90f0a04c75a03ccd848c062e1c7f29e2abe9bda129be3c77c91e23e629f145da2cbed2a00f30ede66287708e06ee088de7ee99c223aa4f38f65a44f5022898c15d8e2208a06b1734219ffcf08845cd75b736a1923d9c5b95339b46be0a62faa92fe446a7f1a02caa87a43c522273917c3361147cf9f65726edb4303bf08472d1f731f7f1d8d180", + "0xf90211a0c28420d85348785ba5dc126acdc3709fddf8d982ec9498669fc36216e9dfd930a0f8d0a6af1beee90f178471abbe954dba601980e79c57e3c0807a564630b0f98aa060d7f22dd75bc5b0f652cd3ecb8ac06894d12f1edd9c8bd44d8aa446a07a2ca0a0ac9d30779d31d49345787542406b47a47cee982b098f151d33d1cb8fb4422c7ca059f7ba64c84859717d418008a134837a085ca251443472a5433f7ff02aecb7dca0905d28e00510bae91e303f7ef5bc9ad99252aca602fc5214f0024b5e113d4fcca0b387f04881baa89f126b43f01e559d38c02082b389846d92317add39db6f2b43a0c22a6f59c13c539ae7211a712baea332f414e5519be41980d401e9812a8934ada0f1a20b64f5fc1921fc80ad36061e6400de3cca9e0f81b1e40489892e386f46a8a08b0c96f3d4a6189e44d73b606f1ad400110014ba8999ce295179af9ada749108a0bafa175ced1fc05c9ea11143f4c2ff78f0e348154b13b24ccc72a2dbfb5045d4a09d22e26d24b0be944879c869d9dda6fcf34459fe2889bb9d77b21b8d9f3fb0a3a0399a64fed0923bcc1f37631bbc424c328e15d7faf5655895cbe10b59216d3e11a0b789109554b6be9898f4ed88ec82fdfee131120c1cd8b65a1b47b51f3a926a4ba03b32eae5ee484dba7e4b915e16d33af10bfb7ccdba135c19d39771f104abeb96a0e7ada21ef9588d859bb8331328fc3d8e508e1803deb4889b6d4a6156c80a2e4380", + "0xf90211a0d89d72e449f7d71982e7dabf525687f75412a20e50da372582a3d30cf1c35fa8a033761c65844f5d8f631a88a0b62e2f4ddb0f11d5a30597e116d65483f1148263a089c4f016714be80532b64396c58f85e0b2ccf5d768cf7ea88a4b3bea7f2c648ba052ada2325c4d91a76941829ff3039c6ee9a33b837be32a2d47dbda450cc48b61a06663670f8764951d4ad3a3f60955fe821ec5a4d647b9114b2d3b281e69b8bdf7a0d6648884656ca7208dbcb8f16f135e28a5a846f34671bf6d0382913fd5485c0ba04761ce4ed9d37b276d2498e2ca6f8e4f9c12392ddf4bbfc9d0b1bf17f5303335a0022e23b9205b23b009df04e033ed0760538f69fbc48b2470fc0ab4212e0afb6fa073f6573791a81540095c2ff7056d78d1084b080300c2ca5b764a01c632c3a6dea0561fff1175e2078722690fe7fb48e1fab8eeefcc784a74b0489d76a83f05d60ea0e6b012d9d9ef37b2e80d0f347e15bb09aebc61a26c8715509262e35078d01101a06091079a6db4b0870d44104184768ebdb2b31ded13cb4cdff8a8b3f83a0e3709a0203a885a27268a159a7efd7fe5cf72aab0e8535e0bfe1044f63d5399eb688f46a046b7323560d0895f8b0146d804a1db2efeee8eb76769e021c92813a850d29702a048a9b7bfbadd3a565a8bda485e709e9c4b18ea85dd40b06972d53e23d86a55d8a0c425bdd6b780766d1cf8d1f45655bd4a496b3689e5570d54340d61c84c8fe3be80", + "0xf90211a08e17568668f65cb4702e2aebe8e2a9f0f67de85b6633aede938ecddc8a920949a09e919bf187ea9d92779ff6cf6a4fb3dc75e3b57b916fd59e20d04e13cade18a0a08985c19b4aab8df147a98bcb3501c9c63528abc9d4be8e3230336b443b0016c3a0179e1699cffb08ea37f5a2aad475647fe1ebec0716a94dad5e24f1ebc7c4a6b8a047cc5138eaf19486119d86d58142f66690c2f488df6691f3daeec1e3244d3bb8a0c0233431ab05153290ae09c135bc4aa71c3edd6e33730d4be92b5d94aea179a2a0573413c3fdea2b6c75b2de53ac8279638d59073e0e484c75047827487ee4fe24a079853d24d31463631a673db7827d1a72d5ddc9e208d53d5f58f4a647bca0c78fa0575665572d86a3a8700afba0a8a2723b5c5bb65df012a67aae23d171f60def8fa0d78f6f85cf19cc702e1ed1cbab0ded6992ead318f3ae14509fa3d55dd9427f86a0ee5bbe32a65d0ba95451de87d7e170715a97bf1d80caea9f47440b51eea72113a0d8061e631b39099d16818188563b0a58e57423a7a13671dab0c4e3efa6ad075da0aa762991e48bb6f9889aebd601a26a4727365ac90f20a3be8c4b1f7cf4e1fd62a09e5153ac97aca36aaaf60c1bdc9dc5455dbdd29a7511c2879821a6a356618f38a096b11709222e19fb2bae5b13fedbfb7e8436718e7eea1353fa320a4295fbe996a0738af15a7c26dd4572af2205f983256588f67b60e243ab8dceb383e8b8bd573380", + "0xf901d1a066d423d34e5a9b16cb92a7b2cdf53f05ab66d3f18aeb38d365777649d13208bba0dde2af802f10a487aa846f0b02ac06bb18c9f508aa4cd2e6db0b1a0d7dc6555ca0130f2594c868ebd78c401c6618a7d602d80e0197d5129ee07d0bef880059a9b5a0afcd23ba8fe2f7c882188bd4a5a8cb7ce87299736495d309b90d92ff87b8aff6a0cd6ba81a6514383d48a5462a7a5367178ef1ad8c9edcbb1377f30522709ea771a098cdd210dcceb35846cf0b0fb25cd89927078dd5188b80985923ff56ec3441f8a096ee188171d9b276721f98abcab6488d7b65c563af3803627dd1c405e1706dd6a077a17405bdc685b354cd585b451222cc6f700b718e00307b06cbfc6e461da14ca063855a2ef44156d7e415e7aca2dff91e73eb882388012b2b943126b715ff46fb80a00741317947e4f7ab90115623ddde415230727c23db69e823545eee11bcd1026da0ca477a6f6494f2973d7aebf600e47061ba702fa28469272cdd48df9f38416a46a09d8fcb47e73f0ec91b70e7d1d25ea8b832f538848079e2df60be7fdfc5978a1e80a010d68c307a7c7227437c9271d90fce3494319650d78bce7a6f35847880cb5e9ba05edcf0a5cde11abb08c394c81d1e78d8063809f806b551c1f2e4a7910c5013a980", + "0xf871a098dff7457df691287379f14f3c86c17181a01e98865801c1faeea0752e6113cfa05825c5dadaec2cbc329e20866073b32e850c67627d0a46f8ecb568605edaa72580808080808080a0c43c6c9016fcc2c015790a838e8d1cf7463ab94893de8981f3456b418286a67780808080808080", + "0xf8669d30f80e30c43f7af65d01662a3164f088eb29e38f1a3a3295a28878b474b846f8440180a015998da144c8d0fdbb79608af9f74834c629f4a5add49b48dee7d27ef45b7b92a023c736713c762f5d684fdda5244e49dc182aa801b78383ddf51015c1597c446c" + ], + "storage_root": "0x15998da144c8d0fdbb79608af9f74834c629f4a5add49b48dee7d27ef45b7b92" + }, + "last_batch_index": 65327, + "l1_message_hashes": { + "1006795": "0x41122df297f0d03c1ed701ec293249771ae3d7b759e79914eb965765b103c9c1", + "1006796": "0x1e85b9695e863751c5ab1a2622d17434aada8efc677982aae4eb980b1ef98a03", + "1006797": "0x28c4d331dd9352075e8530d5a04547c89071543b3fc03dc475bad24acfc1db7c", + "1006798": "0x15725173fee42494d9e38966fdae179147ad38c93d6c1b32aadae5ddf873b5d7", + "1006799": "0x29aa3d061c7ee70f24bce74ce65594b636d524b1546d2b4d7a8f7f1cb5f6732e", + "1006800": "0xbd02772fe1efa157cd479e2857408ef9c3babcb05987f15bc56e109e1853b847", + "1006801": "0xd0a1c8b053d670eb6fde130041dece363a49612ac3c52c781c926a7a4da7ce54", + "1006802": "0xf4301fe88c7461c37e07ce59f587241af8f30dca399d0a3fc238e824647da256", + "1006803": "0x2fdabfe1d43111a63b32a30370bd5ae08c799bdeb1c3810c119626284721f9bc", + "1006804": "0xeab5604293d4f3f2d5cc2a19fb687f181ee61720ece41b4bec4d97762d974d7d", + "1006805": "0xb66b2e4313c135be6fe4ae264bb79560e494e608ef1b46b96eb4ef4644715ce7", + "1006806": "0x808f5830761a232169bcedd7c3fee827cb746425801f6ddee90cedc3489b7da2", + "1006807": "0x2d164d24a57c9fa268fb304940e45bc9d4a31496646477324ff9421535a80623", + "1006808": "0x8665ba6c969d8102d1c7cbf3c8507e922d54377f5e77a955d8af50ff782b923a", + "1006809": "0xf79257a382e1220ceb7a09700f8d34dbb93a30088bc4e0fcc983c80418a3236a", + "1006810": "0x411f72a9b21793236006f6cfd7f50a9e812f80468c3df5b90ec19fdacd886aff", + "1006811": "0xe21b8ce57007781bb839043e3cc349a739dc752249e402a3f89046cb0f836def", + "1006812": "0xfbb6d35889ecea920096892015de541d80e06c71e27bb99725a46ee7fb70ad1e" + }, + "blob_versioned_hash": null, + "l2_ibc_account_proof": { + "proof": [ + "0x092e9fd31e6bd9abf5df5662625dc1178088714ddbfd3d6d3beb635da704ada55622a4888e7896024073f64eaaeab81154e47d9d311e11d1a88fd2a00a681e3d80", + "0x0903cc81a878036f9e6d0a8717d281c284b34a1a34e780eda1a6def30e9dc84fe406c6feff10ff1cd0f83d7577c8043075029c5e8922c455f6938d0ccac88cc69e", + "0x092cf7c56d5230cb2c50223452d16ef72dce99da4a4d80fd7c01f997fd5ef81b8b2ee7d26ddc0e8578b16e57dd02f47cd0cad1279ce50446f35a75499b33ea2db8", + "0x091d7c3867b6b9bd9be10a4d45c5b65b4cf92b0e6176303d02ca1964339d2666a0243d64898d02791fbc3eb5b6020ff0a2cecc063bf25ba05c981ce1a5f74dbe3d", + "0x09282232126cb08638f9e0938426905b6b85c643de0ff58a063c6121b824ae339c1b5a4d10ec252efb0b72397df64a4ce34dc0debca615643c5ce561e3787d93f3", + "0x09274da936258084819672420319e29d5ff18e9906d0c8da7af2fa061549f96d3020de31255a92c4f22ba1d289dc2d5c8816340ac28169738fd333011500f9e9e6", + "0x092bba95d3d09c15eb26a277b35014a3aeaecc62d711de4453395a4e6703cc7d97160cd75e3e39f670bba0f61ab23a0f8e048f71634f83ff331ea7bf5481b73d87", + "0x0928e9f6c9e39670dfc4e18fe857105a3ed9339cc45decde27a4c60bf695e9bb9614f938815429c7eeedd5295112346b3222b18661f5edbf76fa816c1e4f72aaa9", + "0x0912d710c6eda8e2189b7bdfd50fd42459471fa11b5e22e7d7a2a27263c324756e1b81107e291fe3c4e6bfa735d1312e7356b6f69b38340bc1ca06fefa361a67d6", + "0x0919f9542e89e676a66dd6494538a06fc62175bee4899773343f5b4817da4144fe25656d91fda6d72f3451fcc0d57f3a484b871e040896fbd0f66c3e12090caae6", + "0x092177170d5befc5d0cbd497264643eb38d100fd63b1fc889e43727a63a1c6198125825e171bda6951492f794b1f9e2b82dfeef4aa660c74dc97da7baf2c041e07", + "0x090ddb3724f9cecef4cc7d032b10ffdc96e94686b98330290cc6685ed2853043cd1469fd82ccc23306dead359574f6ae03e445fdf1129eff7fd2058abe0a70675a", + "0x0920fd0a7860c7d85a74fd3215f9d677e69ffa1822b4fa2a8b7bb5d8ccd5d6e04314845834df9eee4121577ed8b0321bf3e5f0d34cdfc20ff4a308a0e411811976", + "0x092dc00daa96b42c84e0415b5da62cf0658a3cd8968d6a92a1e49270e9c3ca88d72f85ff486c99a41f4d6479f6afaf86d279e92f7a16b81680654917bd31dae63d", + "0x0901ec6deb4645f02ed95685a43a82186176427c89a90170c163a70db370f1eaa52fa207d906cc5bdcb656cdcc54dd105e5634aeea9c83aadae1058f972b48042e", + "0x091d980034660552a8553611f1fa8d4eb839bd86efb99c5fca5bda905bc5679ae203250814f36101593308ec5db798645319c6fea28ae7f2d2867f9be47e0833d5", + "0x0917638782e520c1ecee645a6cfc3adfa7d23b4656b3fa1e904bbfa5b7a2b6b7b41f6a2e41c3119d75ceb0f555728614ad62b7669292c7a7d16fc85ad946e2749f", + "0x0911a0f83291de5e4353815a7523048458c57469d9625ceec99416792cfe072ac10160b797e91462316ec757c109e294909ee945c817006ba42404d550180967c9", + "0x08002548d5cce112b0b1e8a4167c469a239124ac1df014b8165e29ef8852f8dbe51fe1fa0144e13a8357f4d8e7442d23b67e04fedef9a9883800804a935d0eef38", + "0x080424062475fdd0ac74ac4ead112fbd8d4977c9fef5f670de35176358cebb6c6c16a9a7f36ef9800322bf50bc14f0640b6a4329f3b298c60cbfeac35105296fd0", + "0x082c552da48e88802b202ce036cfef6d2a657c7e36a19ef6d98c5a6d63d53292ca0000000000000000000000000000000000000000000000000000000000000000", + "0x08279c9516d22de4be4b648a1bc0d9345449ccfe0dd34511a437c6aaa5855eb3ab0000000000000000000000000000000000000000000000000000000000000000", + "0x06200fe6e24ca297570942c6c7ae87142cf9d05b652bc76559b51b34918d39c62421dd3921bc6242639b4046eebec2ac92b8fb90fb98dc4d9108da5ed16a8d6cf2", + "0x041d3c5f8c36e5da873d45bfa1d2399a572ac77493ec089cbf88a37b9e944284220508000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e35109a8967e5c50000000000000000000000000000000000000000000000000000000000000000c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4702098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864200000000000000000000000000000000000000000000000000000000000000000", + "0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449" + ], + "storage_root": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "commit_batch_calldata": "0x1325aca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000007900000000000000ff2e000000000000000600000000000f5ccba6a5fc17830df0d590c3d9274a32aa85d804f6194ec280b18ee519f17753256bf9580cce6a9e052c49b091dcb7c7555c147f088858834ce410cbba72d1d7e62a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000001dc000000000000000000000000000000000000000000000000000000000000029c0000000000000000000000000000000000000000000000000000000000000434000000000000000000000000000000000000000000000000000000000000052a00000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000099c0000000000000000000000000000000000000000000000000000000000000baa0000000000000000000000000000000000000000000000000000000000000cac0000000000000000000000000000000000000000000000000000000000000f1000000000000000000000000000000000000000000000000000000000000011aa00000000000000000000000000000000000000000000000000000000000012a2000000000000000000000000000000000000000000000000000000000000137e000000000000000000000000000000000000000000000000000000000000147000000000000000000000000000000000000000000000000000000000000001bdc1100000000002e08ae0000000065d38c3c000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08af0000000065d38c45000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08b00000000065d38c51000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08b10000000065d38c5d000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08b20000000065d38c63000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e08b30000000065d38c79000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08b40000000065d38c82000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08b50000000065d38c8b000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08b60000000065d38c91000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08b70000000065d38c97000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e08b80000000065d38ca0000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08b90000000065d38ca9000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e08ba0000000065d38cac000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e08bb0000000065d38caf000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08bc0000000065d38cb2000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08bd0000000065d38cb5000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08be0000000065d38cd3000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000090f88e83095eae850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000005b0ff75cd83104ec1a09118e3e0179ea5a4d6c3bf2ba36a434071b755859937d54e1af47c4a071382dfa00e6a7035c9146960c599bd4958d1654e0176e95d4474c4cacb4f6ed6e5351b4d00000090f88e83095eaf850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000006628d341683104ec2a05ec1d237325b39d3166bbcd0e5d7d977895bcb9e1ac5394d0834d5c36f91733ba0753bccaaf0438199afbe8ecfa486a2f47eb83105513cbfcf596c2959fd31b7e600000291f9028e128501e36428ca8304904c94bbad0e891922a8a4a7e9c39d4cc0559117016fec80b90224ac9650d8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a40c49ccbe00000000000000000000000000000000000000000000000000000000000254b4000000000000000000000000000000000000000000000000025e2f8711b0801500000000000000000000000000000000000000000000000aded2ee81b1544ae1000000000000000000000000000000000000000000000000000005d6af0de6e50000000000000000000000000000000000000000000000000000000065d38d4a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fc6f786500000000000000000000000000000000000000000000000000000000000254b40000000000000000000000000815bec0de3f12ee8bbc7525bc3c3188e1b9d59300000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000ffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000083104ec2a06488c7a4feedd44b9539a9c2d0abcbd52ecdc512210d021253baccb1e7fdd739a009171b97d457dd4bf527a19bd56d03051f5b8cbe3850c8d264b03c21cb8c331300000219f90216238501e36428ca83039ce29417afd0263d6909ba1f9a8eac697f76532365fb958806f05b59d3b20000b901a45ae401dc0000000000000000000000000000000000000000000000000000000065d38d7100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e404e45aaf0000000000000000000000005300000000000000000000000000000000000004000000000000000000000000d9692f1748afee00face2da35242417dd05a861500000000000000000000000000000000000000000000000000000000000001f4000000000000000000000000daf399baaf8abfa2f307c73bf498437c4da1109e00000000000000000000000000000000000000000000000006f05b59d3b2000000000000000000000000000000000000000000000000065d46d9677084d3e96400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec1a03dc4d29984b5207f2f0779332eaa598d0520f57efaa7095b4bfb133a7800796aa039f533f648e630ccf17c646c22d98e8f726901eb5d74bed3733acffa5218775e00000090f88e83095eb0850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000005cf61f2fe83104ec1a09d1cd81294a6e2b34ebb737f09b84d62b8059ffc9dd73a65608bba6dc7834526a06503ba84a711b8476570faa5c272e513e64952f379d95ca9385d81c90433d428000002d3f902d0823b4f8501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b23203020300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004b6da6f5293000000000000000000000000000000000000000000000000000004b6da6f5293000000000000000000000000000000000000000000000000000004b6ec004680000000000000000000000000000000000000000000000000000004b7101f1a0000000000000000000000000000000000000000000000000000000000000000024204954b3ff5e79b8a128d225f8eff4703d4be0b3e066edfaceacda60c70df1757d73368832028eadaf6c79ce7faedbd73ddbd2ad3e5050cd5dcd8d167e6334f000000000000000000000000000000000000000000000000000000000000000265bc495200f3b8c4b1c83c2a5cc179c0fbee42798fcf2f080e5644677bba4b113665505eb94760c5b683390b81d974ef31e6c59baeb1cc06ca5e6552153d387483104ec2a018c6dc3a48ff3a6cc855a6e2ecee943e891d3ae3b1a79cb0f641a11367f44fc1a05bf2dab7d2da90feba570362cc45f5317dbefad4f9698c043a9853bf9929a7b20000008df88b138501e36428ca82a72794530000000000000000000000000000000000000480a42e1a7d4d000000000000000000000000000000000000000000000000002e71c90e65af3283104ec1a07077a821130eb77ad25d6ef261cec02bc0fea0684f0465aa66a0cea3193e4ae7a002388c45064943f21df9ebf744ded07cf166b1a8aff2ef4edeb082cbfc78e7ea00000090f88e83095eb1850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000005759ddba883104ec1a048bcff9ede17780f51aacc6cd328514901b1160762a19592dfde1c01195e95aba04b9ed0616efd4c02c687d51c4f13693e0e89bd72a4d46db21d04b2ac46d4ac6300000090f88e83095eb2850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000005c0546bd983104ec1a046040c89973c5a4184c34291a3f953eabd67e6addd37fa8415fb22b2990d31f2a01e7a41a11cd2a5edc6e738d66599b2ca2bd5111bba9d6fcfbeb63a30a2ca0de6000002d3f902d0823a208501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b23301030002010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004b7e4f87b27000000000000000000000000000000000000000000000000000004b8181c4740000000000000000000000000000000000000000000000000000004b83213486a000000000000000000000000000000000000000000000000000004b83d5d0580000000000000000000000000000000000000000000000000000000000000000261e53ee5787abd11246dcfce5c86bcdd2be25f557cb6fed11a3959db2a8a8d0378315c31243e1446fac6dea762df98f006c3825fd87d2700e49833c167d96873000000000000000000000000000000000000000000000000000000000000000257f0537c5ec57fe8f094261c68c954cb653ca9bcc85bcc980e5103c9264a96bb7d242adc65591ebb6f486916d5cf6d8b1d3bff5cf0428d99d1735599dafd959083104ec2a007bc8ac3ddc344e1bcec4be99dd2cd374463f65b9693f300af0e9591a579ff3ea079338958a59740f99bc3ef42f7fa7e327f6ee948e45cf739ecd17ae7aebc9eca00000090f88e83095eb3850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000057137d35d83104ec2a0e33a54b970f169753eda72588509c03588ac076cc034e3249103da87befeab44a01d7b6a50495b95f86e9e3da18cfe30a854289502de88188090c6909c4f31487a00000072f8708207728503c6c8519482520894ea102f931f5eaa67d8144f40a536c35d97df6ec0872386f26fc100008083104ec1a031e7f102b58a6f1c9c3987b935ce266159008d1bf4cdcb5cbe81f4fb41d341dda05c3c9d6ffe6a7659d5a80405fb0ae45a59f8ff608cca4a3d4c91372787bba7f600000090f88e83095eb4850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000061f275ac083104ec1a0a60c6e1a5945c62df2e50f399bbe104f4894c6ff54befca73ef846f1fab64d56a050b12389dfd4d8ccbb841548fefd3fc7edd008bcb6d54a3f2f0a5b95716083ba00000132f9012f82acd285024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000280c0000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000000fad0fc0000000000000000000000000000000000000000000000000000000000000001083104ec2a05851621946538766937e5a4e016d4be4b780a5d6831faf7c7a9d81bd7e518553a03d1339a82965f6bfed942c47f1c8fa310d0b56910d3e51d10506cdf1f7a7b57700000132f9012f82acd385024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027a1000000000000000000000000000000000000000000000000007a48b6e5cf231b0000000000000000000000000000000000000000000000000000000338eda400000000000000000000000000000000000000000000000000000000000000001083104ec1a00b1ca424366cc13fd22fda7e7d12f4c57690573118e42df4d3270447be441272a006cc2d1610dece1888f80f7e541d7b3b0639c88ac6d1b7fc537f54f4fa65f19000000132f9012f82acd485024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027b10000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000006748d06b3000000000000000000000000000000000000000000000000000000000000001083104ec1a00a6770bc3298149ef959fdb88201eccac8e2a60f4ab42c3db1e8eda427f7853ea031ce3b30fd95e73c4d7359d8e5884d352a3dcf16716527f4f7aa7508d48a698c00000132f9012f82acd585024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027e90000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000036cc278d000000000000000000000000000000000000000000000000000000000000001083104ec1a0ad0c305e6313bbec9cba5133d409276e2a50bd30659ab28a3a14ea56d92c2a65a078633a5395eae9b3d12481b058833c16d0ae0f92f2a58d99937d9c3b476f056600000132f9012f82acd685024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027ec0000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000009cc29d80000000000000000000000000000000000000000000000000000000000000001083104ec1a076585d6f75194d7d45bb2a9d9743a6aa9d0bcc4368f595c78c74692b0f6ae45da037e876c06c7d81199b6347b05075c186b0f0b424632ff4c6a8b644856e235f7c00000132f9012f82acd785024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028070000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000000bbc12f80000000000000000000000000000000000000000000000000000000000000001083104ec1a0992b006808864294a424c97b6cfddf33b2d69a463950f184fcd53220bf9517c2a07d4ff51eadb3585e268bc389fcacf17e4347b384b93f3911860ebdf974c4ed7f00000090f88e83095eb5850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000673a5730583104ec1a08a915da3832dff84e35d45c2d36b1d35290e2ea953292bd12c0393980bae012aa066943a58aa4aecbbde0ae25f606c99403af3be47bb782c8c2d116df3dfe2226400000090f88e83095eb6850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000057141c98e83104ec1a082b520e880ae77880a704f5f50e487e4d311c6729be0ada16b814a6f7b9b1dcaa0638fdee4253d5323a2a0d07c3794be4abe340a7523831aa235662d6a6540cb74000000000000000000000000000000000000000000000000000000000000000000000bc90900000000002e08bf0000000065d38ce5000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e08c00000000065d38ceb000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08c10000000065d38d12000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08c20000000065d38d2a000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08c30000000065d38d54000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08c40000000065d38d64000000000000000000000000000000000000000000000000000000000000000000000000009896800007000700000000002e08c50000000065d38d6a000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08c60000000065d38d8e000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08c70000000065d38da300000000000000000000000000000000000000000000000000000000000000000000000000989680000100000000008ff88d83095eb7850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000061f68eb7f83104ec1a08fe4af715c9e3fb206242fd72e7bc5ad1295cbe2e51694757c25b490bfb9fcd49f5496ba1e8091e899100836261fab9d7fb8def96d7d0d66eb3f111816ab20ff000000d0f8ce8202b08501e36428ca82725294b5ef491939a6dbf17287666768c903f03602c55080b86467acd47500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000f0944a8f26fcd80083104ec2a0db622cf23b75b702d9180045fafe83e280a27b64fdb62ae243c017da36817d83a07db1cd84da46313447909a8c81646cbe465fdaa2d2f0466bbc2e05c0b3c4545b000002d8f902d5148501e36428ca830a18b194bbad0e891922a8a4a7e9c39d4cc0559117016fec872386f26fc10000b90264ac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001648831645600000000000000000000000047854ece53d37ffbb6eb65877ff364bc98013d7000000000000000000000000053000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000002710fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2766000000000000000000000000000000000000000000000000000000000000d89a000000000000000000000000000000000000000000000031658a5646d1e70ea34000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000031460b6a211e6204d290000000000000000000000000000000000000000000000000023702e605172b20000000000000000000000000815bec0de3f12ee8bbc7525bc3c3188e1b9d5930000000000000000000000000000000000000000000000000000000065d38dff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a0000000000000000000000000000000000000000000000000000000083104ec2a04242dc9767f87f8cb24b6b61fea7953b5ea968a5c0be6fb6a662df7beec340fba00f574fddc19411ce873dc04eeaef9f6dda34cfae722a3947948232049c86bdc3000000d0f8ce8203608501e36428ca827d1e94b5ef491939a6dbf17287666768c903f03602c55080b86467acd4750000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000cc7c561c83104ec1a06a4d3c03bc22dfc394ade44e75363a60714bc4738ecd4bdecc444bbd503de904a0595aa5558067d2e54ede7123fe54b6d557a64134c8e89c65e5b50e4be683243300000178f90175568501e36428ca830f424094dcb58e26413f087312c2be7d8c1b8b10f90b6c5f87753d533d968000b901042a986e340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000753d533d968000000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000003f616c656f3171766a357361726a65647570673735676b6a7436637274337338716d7038306377327539377170683775706178796871767667713279373874390083104ec2a0b866a91a932786d638034757255dabbc62249252ea889390abdcca64f0a15899a02539285d670194825272c930b9ddb4fc96371416501fee818ee25889888220d700000090f88e83095eb8850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000006740f139b83104ec1a04a948507f7f49cc96104846eb3041b2b83516201e1951e8d6d560bdbdf92bc1ba06f81b72afa4551243c1c7ec3eab6d7c52478e2a9f30bc1b93a61f562170d2fb200000178f90175578501e36428ca830f424094dcb58e26413f087312c2be7d8c1b8b10f90b6c5f87753d533d968000b901042a986e340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000753d533d968000000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000000000000000003f616c656f3171766a357361726a65647570673735676b6a7436637274337338716d7038306377327539377170683775706178796871767667713279373874390083104ec1a013633d63e334661c6c78939fd7ba4c824a0a5d5f3309c5d5211ebb79e204f381a032c79737b9321aeb5bef0890dc58cddf208aa51365121d440504a95500862fb900000071f86f058501e36428ca825208944eaf936c172b5e5511959167e8ab4f7031113ca388059ed95aae08a5368083104ec1a04132abc0724aaa8c17528876a63be5df2183c52f86a25e275f4bc2d39dcdc5b4a0494a4eae98b0132378ff74f1109d77ea67eef5214a9463a2bbf3bf7fc827bb6300000090f88e83095eb9850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000005ce0f8e4d83104ec1a0dc27787a41a980e88b835b7f342ee519de3dd9a8aa070021eb9204be186756b9a04dab18c0464b0661715c8d9799234d0864a214dd0cc32128b8e2306cda8e7c510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000195a0e00000000002e08c80000000065d38da9000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08c90000000065d38dd0000000000000000000000000000000000000000000000000000000000000000000000000009896800005000000000000002e08ca0000000065d38dd7000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08cb0000000065d38de0000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08cc0000000065d38df5000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08cd0000000065d38df8000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08ce0000000065d38e13000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08cf0000000065d38e1f000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e08d00000000065d38e26000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08d10000000065d38e32000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08d20000000065d38e47000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08d30000000065d38e59000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08d40000000065d38e62000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08d50000000065d38e65000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000072f87082605b85174876e800825208944c5fd58936784774eb781b0ee72a828a86031640872386f26fc100008083104ec2a0e8214c0fd23f7a7b86ced35d7e9639e38e8824033bc52a84a861d91a88c9eca8a0248bab86bbfb2007e15f427219cef2dabad0967fef9de1f01eb37b2facdf5ecd00000132f9012f82acd885024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027a1000000000000000000000000000000000000000000000000007a775a2eaa833a00000000000000000000000000000000000000000000000000000003dde9f800000000000000000000000000000000000000000000000000000000000000001083104ec1a0bbefba2e763e9da1411ab9482a7c8922608227d34d500ef6980f14693282544ea025d21bf0119f736efab8058aad833e52cf572edb0886b614ea265c30edaf91b100000132f9012f82acd985024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027e90000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000003e43a03e000000000000000000000000000000000000000000000000000000000000001083104ec1a0f96303e235e28696911b9cbbec70cafb130aa4687f9641b256fda618e12bdb8da0600580c1922f4ff2569f5f12a5516cb7c6d67b9b5fcd81eaaa52fbc71a34368600000132f9012f82acda85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000280c0000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000000e111300000000000000000000000000000000000000000000000000000000000000001083104ec2a0050135c9bb5868b1071104d3d5f67b74e561b3e2af1ce68272270371cc90e891a069bb88ffb9442a7bd17fdb0ba982d2343a32ac29a3b3b3dc8d7b0615f0ae4e8000000132f9012f82acdb85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027b10000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000056840a09a000000000000000000000000000000000000000000000000000000000000001083104ec1a0c3e0a60a075f727f110d185b85f3dd8c7df9d9b6d417cd3cfba1cffa79b0525fa01cf73c4bccaf428ef66c8f08521a1b4684b305c092471cf6c2e138783a82303500000132f9012f82acdc85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027ec0000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000008caabe00000000000000000000000000000000000000000000000000000000000000001083104ec1a00c59f5736a8619e2aa98be5a112b1ab190712911a4a43fd0af9b946ac726c7e7a00b46e9927c95ea8ab1cc349e8567da5be7d569d029e8e7470b854c29d2c7c93100000090f88e83095eba850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000582d215e083104ec2a0ec6aae249e321655361487aa21aaf71fd99e20adc17af90fda247a5ad952b185a06bf139de79a71598af69b6c2352058719dcda98ed1399ab660aa6360df7f06eb000002d3f902d0823b508501e36428ca8307a12094917f3e5946bf84e8cf54e4efd00cf5748b34237680b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000020000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000049e5f86ded0f3bf109439689fab8d0de0003b3ad0402000301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000043bcf169ca00000000000000000000000000000000000000000000000000000043bf0ae0d000000000000000000000000000000000000000000000000000000043bf0ae0d000000000000000000000000000000000000000000000000000000043c40d13000000000000000000000000000000000000000000000000000000000000000002c48de2c7c3be5f97d45f4aab21e615645080c672bf5b821c3786d928de16f1ab366ed40516977fefa539dfb9e1eb2754307ff98997a3206ec18227df546f20760000000000000000000000000000000000000000000000000000000000000002109e6c708122074988608fdb7fd8b973d6306c5697d7533df48151e757661f3722a3cf95187f2ec13e2ee4f67be648bbe949518abef6e2bab1eb9a90f032072483104ec1a014234d252de2ae2204427f3e94a054c1944e46a3df256d31ee9cd1f0a288f342a03db34d53d1623c3339e439c2c433e2c4d48e2a0a23469b430e2172c6e50c4db500000173f901708207bc85025c3d32fc83015f2394a38c2b5408eb1dceedbec5d61bed580589c6e71780b901040000008200000000000000000000000000000000000000000000000534ca5face53480000000000000000000000000000000000000000000000000000000000065d38d640000000000000000000000000000000000000000000000000000000000000060baa6fdb919c9b7f0988a0ce25ac1a1643b6525d0f72b75d6e7501ca1a83f0486000000000000000000000000637052ec2c92802a424d3647149b99ea97ee347d000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030c5c75000000000000000000000000000000000000000000000000000000000083104ec1a030577022ef594746076bfffc5298ce10cf3414196ffff6e514f13c0097c2f52ba02312d3b33478f8c937d00f118bf993dec21e1c8c0a559d5faa012cc13335c383000000f0f8ee82041f8501e36428ca829f4894b3a97684eb67182baa7994b226e6315196d8b36480b884994948fd00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f75e553fbe0dc1bbd9efd21cc4408e91bf9486b1000000000000000000000000000000000000000000000000016345785d8a000083104ec1a0e39bee384840e13f1b734f679392f657018c9a039cd97b103d8a22039bade685a010ee03b9e52ff8fdd631c6d003ac4722c10c84329e569b675bd383cf274f973800000090f88e83095ebb850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000005335bdb6b83104ec1a08f4e6fc381c41ffe67506a9e116e5b8e480b2976c950273a177091244756e7a3a02ca25aeca35e728593530fd227baceeac6299b65e62d4835ea41eb0f3b2907a6000000b1f8af8291048501e36428ca8301bfff942508f26127313db871b9294165f58feb9fd1a7a680b84417835d1c00000000000000000000000000000000002c76da000003e8000003e8031875ba0000000000000000000000000000000000000000000000000000000065d38e1383104ec2a061310bbcdd115f5fcd3b1c7ef0d8638f695f7d2a3dc7b8b2880a6d44607b71b1a0656fa0fbc5393c4f51da6e431ebb802fa34667beec7b1de61550017933ec96bb00000291f9028e158501e36428ca8304e31f94bbad0e891922a8a4a7e9c39d4cc0559117016fec80b90224ac9650d8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a40c49ccbe00000000000000000000000000000000000000000000000000000000000254b5000000000000000000000000000000000000000000000000a7912ede44c9bdbc0000000000000000000000000000000000000000000003034c8a5b22040434f40000000000000000000000000000000000000000000000000022a0a8669f02030000000000000000000000000000000000000000000000000000000065d38f24000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fc6f786500000000000000000000000000000000000000000000000000000000000254b50000000000000000000000000815bec0de3f12ee8bbc7525bc3c3188e1b9d59300000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000ffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000083104ec1a09ab2ffd3d217ae34677d5af7823d0884628061c5adeb533ccf0b74304d46228fa05782c1b3b74eb37cbfda4c01030914f939662c5bac901c76f415dc8e66da7fc500000090f88e83095ebc850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004e4292b2a83104ec2a050ffda899791bd40dc372236190e2e3786bc9c3e4fca8ffbef8e6f6867cdca31a034635bd4f2c18cc31b4bb5156b92e3662281aa1ef4c0781dcff75edc82667563000002d3f902d0823b6e8501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b23903020301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004b8ee227a76000000000000000000000000000000000000000000000000000004b9644e8516000000000000000000000000000000000000000000000000000004b98e212e00000000000000000000000000000000000000000000000000000004b994170f000000000000000000000000000000000000000000000000000000000000000002a6eca87a16bebd982ff5d3797b8579e682d491a36827ca23e11bef8c35641283bcd32027d9ba36c78bf33a6767274bbc89d4f33fa11f9e7e9599e8b74082b8d600000000000000000000000000000000000000000000000000000000000000024d9bb7f890356ee1d5ae89b80ae916cedd9104f17ce9dfaeacdb5df621d2905c4361affa0ef162e125c968952fd0d2b14dcda26448dea738da139272e23b983f83104ec2a0ca9e51cd707be2dbe6c3132f6535a23e7fc0d7ea49402ece3cb275dc40593b2ba00b1606e416c166f7edf7ca0b4758fa64859cdbd7800a63e9c1c9f1fa83ac2c8f0000008df88b168501e36428ca82a72794530000000000000000000000000000000000000480a42e1a7d4d000000000000000000000000000000000000000000000000002386f26fc0ffff83104ec1a03d891006607009f1a3e59703f082fbf85feaf0bf447e94d072cd180002a38af7a039e20c2406a510739af68c6ab8c23ba532613f63d7608129a9c984020e0c964b00000090f88e83095ebd850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000049ab21c0483104ec2a080fe011eba2477490f88f7e195c2a403c25c8add23748d603aa1f468ca997ae2a06a1f4ec52b952e78a1e82fcfa76d2da1be87efe33bfa555c11ac84ed2fb3fc7000000090f88e83095ebe850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000052e0683ce83104ec2a0cfe45c42a2366220055c653998668efba72d020ded987dc81344588d5d503a46a01045369d1235a31ced0ef38d5fb9c3b954240e49752f412a65b5bbcae259a409000000b1f8af827de88501e36428ca8301a0f6946f1da9076f36000d5f9decdebd52402b1410b2c580b844468021b70000000000000000000000008b38e245c3dfa1ce036731f05af94e24b50f3c8f000000000000000000000000000000000000000000000000000000000000000083104ec2a0e720d4ae2f084c988f22870e10bd3df11812c45be4e38511e15e0c02500d9e88a0649233f964f3e50aa5d19f1b837249846c3cc967cdea3a220d968490e0bfb1760000000000000000000000000000000000000000000000000000000000000000000000000f320e00000000002e08d60000000065d38e89000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08d70000000065d38e90000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e08d80000000065d38ea5000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08d90000000065d38eab000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08da0000000065d38eb7000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08db0000000065d38ebd000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08dc0000000065d38ef6000000000000000000000000000000000000000000000000000000000000000000000000009896800003000000000000002e08dd0000000065d38f11000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08de0000000065d38f17000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08df0000000065d38f20000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e00000000065d38f2c000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e10000000065d38f48000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e20000000065d38f51000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e30000000065d38f5d000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000090f88e83095ebf850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004b9980ff083104ec2a0697a4aaa399985973e586c32551d8dc55e01156fbfcc767dbc472d6f93679d9fa024fdf9cbb9dabebedb87a5966c3097bd5dc123fdef0fa9286ecb2d3878d927a700000072f87082605c85174876e8008252089436f7056d1f46509952065a599cc1ff4e7176b0e8872386f26fc100008083104ec1a04c166889902e4cd8a5d38b6c78eb18a17f79d3c2be6394d0ed362b492931b080a0564768b6be22620b77c35e423e9986670f7f1e8ad4305f4a5417d986b3dd381700000073f8718227b98501e36428ca830186a094f433434b7f5aeb00695f84241d48d3c67d8f19bd87038d7ea4c680008083104ec1a0f954b22faddc89fec56fb308c748c01a66b7b240d7e40def184293b756d62c2ca04f2b157d7dfc094358be92c1dc88d710bc950ebec2da543b7ad9a0cea77d3005000000b0f8ae827de98501e36428ca82f9d9946f1da9076f36000d5f9decdebd52402b1410b2c580b844468021b7000000000000000000000000f433434b7f5aeb00695f84241d48d3c67d8f19bd000000000000000000000000000000000000000000000000000000000000000083104ec1a0a70ef1d6a32dee067ae7d5cd3a4aef696fc5a367e079c0941c40537f46b25a03a007029a51a9478980712a50ccfaf47de9e0e96c925cc570f40ffce02c340f6f5a00000073f8718227ba8501e36428ca830186a094ccbabdc2215fc5a34b81316822f1d164d161f8f387038d7ea4c680008083104ec2a0d5394612dacd9dc892051e8c61ff0409f274c32530dfebd9c7a23111ee159594a01b73a4a25ad4e736c4fb9bae230ec03718da68df20c34433e5a6267bc240f699000001d2f901cf81ac8501e36428ca830311d7949e4660807889622acace89cd8445e367703a2b4280b90164cb6e6ef63566373231313361666163393436316161386336386662343762363034643830000000000000000000000000c629fa8b87ad97e92c448e56df9d979e1d1f441f0000000000000000000000000000000000000000058569cc491bd36692e0000000000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000029a2241af62c000083104ec2a051dd300e3a164b6daa63d120bde70d6049d8d1e6715f729e6ad316999f412b48a036b214d1c1fc0c989ef6bfa38f2062ccfabc8b49cb00a957c4ea215c8ee0849a00000090f88e83095ec0850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000544ee9f9b83104ec2a0469b8673ef0b0fcd6c80f46ea1ad310f1debc1bb6217f2d3f95787df71b8cef8a041403731e3c74f9402cbc38ec240da70b51128cf7b82e1acc9acd1901539e0e100000132f9012f82acdd85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027a1000000000000000000000000000000000000000000000000007a2aee7691eb7300000000000000000000000000000000000000000000000000000004a3e59000000000000000000000000000000000000000000000000000000000000000001083104ec1a0d00e198e118a6742465892d21ff3222da1ce63eaf8116e9e7d7beea1bbe4b49ba00888bcb383e7dda2e94ffc5b4c4bcdd8b112a1df044ecd71588c378508ccc5e800000132f9012f82acde85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028070000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000000a4824200000000000000000000000000000000000000000000000000000000000000001083104ec1a0da2c21cbd6dd1d6fe0535e7106ccecddff3e8c90cd76106fda7688ce11744167a0144b6b2e34666b440d1b4c5851f08c7b76ce80a28c802638bb669f6f3ae341b700000073f871822c6785024411ca8c830f424094f4d9629af20dbc03d8c1ebb79cfbbafca709ef2a88016345785d8a00008083104ec2a0750f98b21080556831cfabc56d11bec57bae1948476dbbde8f64a692d6a273ed9fe7bf713dc768c2403477d006dac9fec6119c3ff679c3e85b2d42a89a20fbdb00000074f872808501e36428ca82ca909453000000000000000000000000000000000000048757c084e5f3c00084d0e30db083104ec1a05e0f3389e00cb6464b691ced0e1e265f516d0205871c3b88317221970372cc54a07c1d949bde5fe62e0324e9c2717f948058bae36356e9e7ec50aa531311a59ba900000072f87082605d85174876e80082520894b2979f6a35f4e127ff92d5be185a641b34882626872386f26fc100008083104ec2a015783f5d3238ab5e2a68ddd1b73d02b5a2460aca08efbf9ec2daa2d23adf5e42a043790cddfb7f1981d3beb431a596683e67dfe2fbbd57b6b43e73bc3550d6522700000090f88e83095ec1850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004dc58c0bf83104ec1a0ad7dbb5642815660e2ed005d6e4df28aabb7cf7aa352e34cf5d411ba58dcaa45a026dc757a8ef033aadddf8a53172db170e9861bb4631ab8bdfdd73c5047487f21000000aef8ac018501e36428ca82b4cc94530000000000000000000000000000000000000480b844095ea7b300000000000000000000000017afd0263d6909ba1f9a8eac697f76532365fb950000000000000000000000000000000000000000000000000057c084e5f3c00083104ec1a0bcd17a46a7396ee809f2fc92d39ca7ccc3dccb6eb11a13304f90498ba7beda11a065a1ab5f65e9ea09a8461e74bcd0e3a4c1c89dfc23ce73847ff6ba18fed3af6000000090f88e83095ec2850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000047c30773183104ec2a0f09a465db2c783b9c332daad4f29fb738e8a82a0feca86eabf0611894b3de66ca075f45189e819674f1e4ef29af4f864de347f57a4dd5f86960c33ca890b575c7400000090f88e83095ec3850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000050b90b6b983104ec2a0919f4257e13c14849ef2daad210877cffa43726054699b774a4acc6d69311aeea06459bac9f11fa60f30099709683f97c7cf24e1e5e61e73a74965fe4036bd1b2c00000090f88e83095ec4850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004bad62cff83104ec1a0655913476335c66dd0346fa06549d10fe0c438e19e270991696a35109077a77fa03fba33a0981caa9ca320135eee74769de161330001482d24866330e37a1fa1a10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000173e0c00000000002e08e40000000065d38f6c000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e50000000065d38f84000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e60000000065d38f90000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e70000000065d38f9f000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e80000000065d38fa9000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08e90000000065d38fac000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08ea0000000065d38fc1000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08eb0000000065d38fc7000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08ec0000000065d38fd4000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08ed0000000065d38fec000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08ee0000000065d38ff2000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08ef0000000065d3900a0000000000000000000000000000000000000000000000000000000000000000000000000098968000010000000002d3f902d0823b518501e36428ca8307a12094917f3e5946bf84e8cf54e4efd00cf5748b34237680b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000049e5f86ded0f3bf109439689fab8d0de0003b3b3040200030100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000004415aa7c930000000000000000000000000000000000000000000000000000004418774c390000000000000000000000000000000000000000000000000000004418774c390000000000000000000000000000000000000000000000000000004418fcd94000000000000000000000000000000000000000000000000000000000000000029e0d92806deebb2914cb5e5dc2b5588f84dd47fdcf134aa5ff9b893d21d59cb10696aa542caa238f1d835936e3e2308c8f13baea651865b8f49b9c22516d271f00000000000000000000000000000000000000000000000000000000000000026feef1c48842769771fbb62d466c64619911e3950a6060327a5693e31092d2bd3de755ab0c06602379a35bcac9aeb8f0bfc4045a59099d6493240b96ec25dc8783104ec2a0a10f1d0393f96ea7169ffde5c31035b1b5d582d0b8615ea93fd0ed2817a8a57da002120b060a3a65ac912de1d102906a12c021c21ab26581e2f107e3da048c516000000090f88e83095ec5850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000051766720483104ec1a0edf42c0bad043adf9b65f14cfe68ab172c6832225bbda56c4b5e5bdc718ae386a03f2912241160726f6dc07aa654bec6fd48f6594253de33a39a3ae9e15a20288400000118f90115808501e36428ca8303970f94ba50f5340fb9f3bd074bd638c9be13ecb36e603d88013fbe85edc90000b8a4b2267a7b0000000000000000000000001e42d0891a7328b650e9aebca35c135cc1693ee1000000000000000000000000000000000000000000000000013fbe85edc9000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec2a0104ea7351f278dfcc3b66d6872728fe4f3556c836d0d546838a59e323b6f55d5a0376ab1b25403e1a397be9712b634cef059380e0cad64b720976b2fea9629eb45000002d3f902d0823a218501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b23e06020301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004ba25be74e8000000000000000000000000000000000000000000000000000004baeea92b73000000000000000000000000000000000000000000000000000004bb58ab0b40000000000000000000000000000000000000000000000000000004bb76e3400000000000000000000000000000000000000000000000000000000000000000026b2a9bcf82852493395492ec6190b0b683e582cae893c5b13457e1d5638dc12261e4c6d3d2d7b77b6413c36936d9e301a717dbb2f4c32bb4917609a7b7ad0dc0000000000000000000000000000000000000000000000000000000000000000201e0eeb368a3ff106da8367a7233ef5bf229a2284e261afd4d142f77a343135604ffcc9b22ea9cbba195731e5adfcbe96003c172fe1ea7b02f0760e42367960283104ec1a0d0b5dd97edf8b8d8ea28506cd8606327dc41c4573226d58ae08a5c3c63643d29a034489b3854bf5766f8e968d6cde2903ca0694a0fa5e75b8ab3b010ec9c03bc3b000002d3f902d0823b6f8501e36428ca8307a120945ed6390a45ea1a6a63013caedc0f0d2baf67fc3c80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000200000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cab70a5dff6b87a6b89050b9ac72088f0003b3110602000103000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000772cc5f300000000000000000000000000000000000000000000000000000000775a40ac00000000000000000000000000000000000000000000000000000000775acfe100000000000000000000000000000000000000000000000000000000775acfe100000000000000000000000000000000000000000000000000000000000000028d1444b61d29fefa7e07d9371612eea94cd81005f72802ed56ae9bbb2825b1e0890f927995d97b97694f48fd7f1bad7d4abb09a8680918adb99ca100e81ff66c00000000000000000000000000000000000000000000000000000000000000021d7180c5e68bc437e07f045764fc7578fbde2e1e285cbe15c2f2303241f172b239624c6001e80ca655d9c9bf7a68771fd7420dfc031aaf0fac44801fb93c5ea583104ec2a01ccf5bab72df95040e40cb3f638c77908f8aba29373b8e7cd35b612488f5f79fa010c42cb4a638f9b49ee3033ec4fd86655075f0eb76fa721b0289ac08918218b3000000b1f8af8291058501e36428ca8301bfff942508f26127313db871b9294165f58feb9fd1a7a680b84417835d1c00000000000000000000000000000000002cb5f0000003e8000003e8031a6da20000000000000000000000000000000000000000000000000000000065d38fa983104ec2a029650fd48127a23a785350843dc22ce0ebad793555bae0251ae5248600d23312a038ad7480f0fca02b4b4aa5cd60ad8363e752fcf7973256cce3c336fecff32e2700000090f88e83095ec6850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004c403be7c83104ec1a03e3876198742dc043d14cf2328cf9b12836945f60930d5fe5ca00c95dcab0a6ba01821953f5191c5e178f548e960bc385ccd5658062d6de76151cb57b6ab258888000002d8f902d5178501e36428ca830a18be94bbad0e891922a8a4a7e9c39d4cc0559117016fec870b4e8cbdc302b6b90264ac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001648831645600000000000000000000000047854ece53d37ffbb6eb65877ff364bc98013d7000000000000000000000000053000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000002710fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2766000000000000000000000000000000000000000000000000000000000000d89a00000000000000000000000000000000000000000000000fb88ef7839f46e847e000000000000000000000000000000000000000000000000000b4e8cbdc302b60000000000000000000000000000000000000000000000fae88de493d0aa2eed000000000000000000000000000000000000000000000000000b474de86fd90d0000000000000000000000000815bec0de3f12ee8bbc7525bc3c3188e1b9d5930000000000000000000000000000000000000000000000000000000065d390d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a0000000000000000000000000000000000000000000000000000000083104ec1a012a7efa8c33d92d5b6c62892d5038e9ff8a56a83fcca78dc08a7e2a675e13274a00dcd2dee7cbca4c55bd4b494474d02db594a6a5b5a71bb32c6431390e339c34600000090f88e83095ec7850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004751c680683104ec2a0cd7b89e7c8ee3c3ea98985eb1731ff0c332a9c8be3a36090aa4e3ec8d35b09e0a0674fda09f9328282b39cee60a20f941eab76382b0a7c5a8be3d58e7a2fb11eee00000211f9020e028501e36428ca830291ae9417afd0263d6909ba1f9a8eac697f76532365fb9580b901a45ae401dc0000000000000000000000000000000000000000000000000000000065d390f300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e404e45aaf0000000000000000000000005300000000000000000000000000000000000004000000000000000000000000d9692f1748afee00face2da35242417dd05a86150000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000853151a55a64182ab87e53629d0a27246b867d2a0000000000000000000000000000000000000000000000000057c084e5f3c0000000000000000000000000000000000000000000000000507ebb1edd24f708f500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec2a02268e50041782673043953f3071101e93d18a4dc1920031870f1607ef28ffebfa006865aa2d740e01f2f242a48cb5c8fa906abe7e2e9dc359c28f225c828925cc900000090f88e83095ec8850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004b3662ee283104ec1a029f7862b4eeb612ff989c59cad073babf2d98634df1b667caa1a98282b8f9a9ca02ddcde52deac3399ee7b54ac6a8265f2bbfa45ccefed84ea7d7406ca1b859f0d000002d2f902cf823a228501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b24004020300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004bc410231e0000000000000000000000000000000000000000000000000000004bc88853fc4000000000000000000000000000000000000000000000000000004bce86fbe00000000000000000000000000000000000000000000000000000004bd2f20aa800000000000000000000000000000000000000000000000000000000000000002d08e5c687ae6c257a88dcb84a1d9890b56641bab97cc37aee856ded8f3da2e40d0e85cbb3e2d1fcd9c60bd8f8cba0f424595fca3700d575a374b4c6a56f7993f000000000000000000000000000000000000000000000000000000000000000279ded0fd9af98e708cd6ac3841282db13c60997a83139831322f2ac46bd02b893d5a3c1d5a031c25803ab32517bef6782726dfbbdd31bab70e2a20b34431338483104ec1a0b1a2eb5742b71da63eb2408f161efde17d17e9d4868ed5c7a09d0fd31ca825019fbc02afdc801daaec9d53535aff2ed2617eaee13ad8fd54aba017684b8030f600000000000000000000000000000000000000000000000000000000000000002f8b0b00000000002e08f00000000065d39010000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08f10000000065d39026000000000000000000000000000000000000000000000000000000000000000000000000009896800005000000000000002e08f20000000065d39041000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08f30000000065d3904d000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e08f40000000065d39066000000000000000000000000000000000000000000000000000000000000000000000000009896800002000200000000002e08f50000000065d39075000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08f60000000065d39099000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08f70000000065d3909c000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08f80000000065d390ae000000000000000000000000000000000000000000000000000000000000000000000000009896800003000000000000002e08f90000000065d390b2000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08fa0000000065d390cd0000000000000000000000000000000000000000000000000000000000000000000000000098968000010000000002d3f902d0823a238501e36428ca8307a12094917f3e5946bf84e8cf54e4efd00cf5748b34237680b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000020001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000049e5f86ded0f3bf109439689fab8d0de0003b3b6010200030100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000004469604c3f00000000000000000000000000000000000000000000000000000044736d026000000000000000000000000000000000000000000000000000000044736d026000000000000000000000000000000000000000000000000000000044756f3b00000000000000000000000000000000000000000000000000000000000000000298982789ee39041e612e1758ebb0028dcb2cb82940be553d9584f26740bd9bb5a9efd4b8ba73bb0ce67377133f78feb9becfcc74691ae8ee96d1d34b2312a55d00000000000000000000000000000000000000000000000000000000000000022165fafa71b8ebd91a65bc20ed46a138232edc400b3dd718604ccd0d6847c516171f1d194edef05d5f6ea4e24ddf15874837ebfe9542b873210c6ea25da172c183104ec1a0647a5cb7c47e177e55458f8bc58e71c847d7805ccbbb1bfa7ce89f5b9cb270eea04c36b9f4c1f58f85447dff4a97252b7e23fc59c9d8fdfd541414684604bbf92600000132f9012f82acdf85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028070000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000008fa5ae80000000000000000000000000000000000000000000000000000000000000001083104ec2a02cd07aa7e8a0e4af78db27a9a1dfd158d9f4d66d1489ced4ed1b9a9fcfe73fa3a0285863b1f7487f05ab3a68dc91ed648d565cad52977c38a5e6f986cf372ffa7f00000132f9012f82ace085024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000280c0000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000000bfb0440000000000000000000000000000000000000000000000000000000000000001083104ec2a0b9e91b376bdb01c12a43d27425b94409f1bdef86a61f5f9e4fec1fcde76d00afa07fb251fb1ac25d942fdf96db4413dc986f58e62f3082a7155e1f13d4f0892f4d00000132f9012f82ace185024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027c5000000000000000000000000000000000000000000000000005bbfcbedd379600000000000000000000000000000000000000000000000000000000002faf080000000000000000000000000000000000000000000000000000000000000001083104ec2a0fc64ad4b584932b3b8a34d4891021326c73a05664b3b36f4f1dc8f7375ab247aa05da67c20907b016ced0fbe974595064a4856814f71c0825f72a3eb659e0ef56a00000132f9012f82ace285024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027b10000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000004af28ae97000000000000000000000000000000000000000000000000000000000000001083104ec2a05e059d14ce3710eb19586e02d1ccd63aa9ce84b46914eea05c74b1482af48ea3a015457f232960c4741561fd0b04193bb666a91fc6719e07ac4761956ce3e4557200000132f9012f82ace385024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027ec0000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000077ce2a80000000000000000000000000000000000000000000000000000000000000001083104ec1a08486e0123e110a57ae9dc6e3bac3acf14c324e932210b1bd0ef31cbfe214edc9a032f1990b2f33ec4e13f1ae04a3cf4f794d0b7b078f5bb9d0d480b03b139160c000000090f88e83095ec9850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004f1f69dc083104ec2a0ea1ec60d5006197e0a48fc98e925def9e26670e1a3ddd7566bb62efd7f04db5aa03dbd489671445b98789e38d681264cf9da50071dcdcf0768db6e53702ebf60cd00000090f88e83095eca850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000554e3d26d83104ec1a0b056d088ecc0d82cccd07de26fba5ebb47a4880c3cc09e650d7d4da46549537fa01ab6101ef79468c9c60a483a22993caaa02c786f44239183b2aa5fea3209560800000cf3f90cf08207bd85025c3d32fc8307b39b94ca11bde05977b3631167028862be2a173976ca1180b90c8482ad56cb0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000005c0000000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000aa000000000000000000000000067fff0c6abd2a36272870b1e8fe42cc8e8d5ec4d00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010400000082000000000000000000000000000000000000000000000076e127ef4e511e00000000000000000000000000000000000000000000000000000000000065d38fbc00000000000000000000000000000000000000000000000000000000000000608268019e9ddeea51158045af6f0b35ae4e4db6441e5ac97b474bece983d8d3c90000000000000000000000009bec2a494c0969fa633ac6f6e32f564fd97bc0b4000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030c5cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fadf055f6333a4ab435d2d248aee6617345a4782000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104000000820000000000000000000000000000000000000000000000003658bc5585b32b000000000000000000000000000000000000000000000000000000000065d38fbd000000000000000000000000000000000000000000000000000000000000006076abb1df9a5a755eae6f870c399df62f33ea0433859494906b2c008e1df2bb43000000000000000000000000e297afb7bbc23961ce7ad7e15f01b716ebe2f8cf000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030cc5d20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001804969b296e89c1ddb1712fa99816446956637e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001040000008200000000000000000000000000000000000000000000000000c85667fee260000000000000000000000000000000000000000000000000000000000065d38fcd0000000000000000000000000000000000000000000000000000000000000060663a751e328a1d433b40f87f1aeb9626d37bbfc9e2a861b6d68e79ced10e0321000000000000000000000000fd500856a57870a208acaab92499eaaa6295dfb9000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030c5cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c8a1f9461115ef3c1e84da6515a88ea49ca976600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001040000008200000000000000000000000000000000000000000000009ee2af9351ffe400000000000000000000000000000000000000000000000000000000000065d38fbc0000000000000000000000000000000000000000000000000000000000000060ba5f36db1561edda56a7e9e58e4cfe714216824224a214f38b02a595c52e7de70000000000000000000000007fe44bbae0ba869e707bd6030f0c104fee5832e4000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030c5c75000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a53dc5b100f0e4ab593f2d8ecd3c5932ee38215e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104000000820000000000000000000000000000000000000000000000002f097c4d3ee250de0000000000000000000000000000000000000000000000000000000065d38fbc0000000000000000000000000000000000000000000000000000000000000060cd44b626c199acec0c8fd7085747c972744379a3227f7ae062f5fbf3ba356cda000000000000000000000000eeedf9a993f7bda00e2dcc6c9dd6f8436d58a2f4000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030c75c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee02370bac10b3ac3f2e9eebbf8f3fea1228d263000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104000000820000000000000000000000000000000000000000000000aea5ce2229151e7a4a0000000000000000000000000000000000000000000000000000000065d38fbc0000000000000000000000000000000000000000000000000000000000000060c3231ab28d32c66956d3d78801dbaec719ac8049a05e624c2faa6808efe73946000000000000000000000000b4f4e7fcb24a2c78e2a22d9cae0f4618ee670bfc000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030c5cc5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9bb81d3668f03ec9109bbca77d32423deccf9ab000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000104000000820000000000000000000000000000000000000000000000b7e45d0df9d705acd00000000000000000000000000000000000000000000000000000000065d38fbc00000000000000000000000000000000000000000000000000000000000000606e5fc786ae1ed2aa4e31d287f82d439b6c8d50b0fa148cf9f1776eb742d49e590000000000000000000000006ca30de62e5087b0b706d6d16c284e0bcd1e11cf000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030c5cc500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec2a06824e9a90f9344a509eb422f67be3c35dc8ad9191f4ce9abcb9383f458d5a5e2a0544c97d6a550fb47d9e8a78e8fadb7a555c46a2e596ba7e609c7a71f0a26d87c00000090f88e83095ecb850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000004cf384bf983104ec1a06cd748f294fd61d74de30c915e53987dd9ee09596bfa5fb8fba915149d508ee3a03d0dd1900333c77943753a99441ef804ff999b81220be522c30aa6c36c2ffc3a00000af9f90af6808501e36428ca8304cdc8947024030c21aae0e8e9ce9d8c6f23ad024a7d162788016345785d8a0000b90a84a11b119800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000005300000000000000000000000000000000000004000000000000000000000000086d8d30822086941729df294f0e52e42edc17f9000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000007739e567b9626ca241bdc5528343f92f7e59af3700000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000004d0e30db00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000053000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000844ce654c1700000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000053000000000000000000000000000000000000040000000000000000000000001e24b33b51e36f3f2391e7e4b51fdab2e772604300000000000000000000000008f5c28ff0622fef758c2c3c2a5eaeeb63d60d4c000000000000000000000000b299eee0ed46b7a34c01f2a01fc83a0b45aa88af0000000000000000000000001e24b33b51e36f3f2391e7e4b51fdab2e772604300000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000200000000000000000000000000086d8d30822086941729df294f0e52e42edc17f9000000000000000000000000000000000000000000000000000000000000026000000000000000000000000008f5c28ff0622fef758c2c3c2a5eaeeb63d60d4c000000000000000000000000000000000000000000000000000000000000058000000000000000000000000000000000000000000000000000000000000000640000000000000000000000001e24b33b51e36f3f2391e7e4b51fdab2e772604373796d62696f7369732d746573746e65742d6170700000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000020a121bdfaa7657e94c06b77c7f3eb0aff9751ab00000000000000000000000070470021f81e30497f86962e889bc732ca70aee100000000000000000000000000000000000000000000000000000000000002e41e859a05000000000000000000000000000000000000000000000000016345785d8a000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000cc9f8896896c6ef44f2504a6a29e6057adbff1790000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c48f6bdeaa00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000016345785d8a00000000000000000000000000000000000000000000000000000023527431553ccf000000000000000000000000086d8d30822086941729df294f0e52e42edc17f90000000000000000000000000000000000000000000000000000000065dccb0c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c44af80948b8a20bb781277559457ce0f2893b8b000000000000000000000000000000000000000000000000000000000000000200000000000000000000000020a121bdfaa7657e94c06b77c7f3eb0aff9751ab00000000000000000000000070470021f81e30497f86962e889bc732ca70aee100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000264e66bb5500000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000237fe4b2395d470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cc9f8896896c6ef44f2504a6a29e6057adbff179000000000000000000000000c9fd2af244fefb31a62a5a33b9d6261cec2cb7aa00000000000000000000000070470021f81e30497f86962e889bc732ca70aee100000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000240000000000000000000000001e24b33b51e36f3f2391e7e4b51fdab2e77260430000000000000000000000007d8b7b5f663e93d7f8970d0a61081af03c63bb860000000000000000000000009f81facae42a7312f49a3e27098fc4d39e2c550d0000000000000000000000001e24b33b51e36f3f2391e7e4b51fdab2e7726043000000000000000000000000000000000000000000000000000000000000000573796d62696f7369732d746573746e65742d617070000000000000000000000000000000000000000000000000000000000000000000000000000000000000447647691d00000000000000000000000000000000000000000000000000237fe4b2395d470000000000000000000000001e24b33b51e36f3f2391e7e4b51fdab2e772604300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec2a045a4b9735ade732b42e3cedc489c1126c58c3908489a05d6d9220183169ca39ea02b9c54809b74ae5ea8a7ea3e3bd295d519d62146966224ddcdd3d0091e208b9c00000090f88e83095ecc850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000487835a4c83104ec2a04d5533d24c916a45ec56b751c2c1e204556925a4648fcb7fc2f80f980c48fa95a0530608982e184de51ea715c62831a91be96e3e8402d59cf853efb6af5eb40376000000b0f8ae8203e88501e36428ca828082949ad940b25dae3bd975d6ed8892c63d5033ad1e6180b84475edcbe00000000000000000000000000000000000000000000000000000000000000000cfe32d6fd614b6766d245a4ed34a95f2fbd661421d9ccb7b5f39d902dd3f9b5a83104ec1a0639b6d1dc1784d3e77d871c62a210f5121bf15bdabcd33ef9d67c0a175c49418a07bde5528b6a4a1a423152424a7e72e3790f291557ce4d88de008718dacd35323000002d3f902d0823b888501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b24206020301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004bb6f4ee179000000000000000000000000000000000000000000000000000004bb8e12eb40000000000000000000000000000000000000000000000000000004bb8e6e78c0000000000000000000000000000000000000000000000000000004bb94b0a5000000000000000000000000000000000000000000000000000000000000000002774a8e4d04db17f63c31246d580f0cd235d5dc3eea4d93de5e403ba1e5e53dd20f5cf9e3962f98819aaec42bbec54ea101b4e9574687d2a31e9d03a0dbcd34e1000000000000000000000000000000000000000000000000000000000000000263b4631075d058beb9465d439635c4ea9f043c534435b3276e213621e8d0acde0d9eb27fdf1a99c44be80437fe52292865d20ae93a1f420f2a74498ca6f7f0d183104ec1a08c8036b99101e41b158ec0ef49218e4ed9033a4ef58d79e76a3f8a18e923d763a04be18c6914e306d52da6abf5b30f1b78c3939cf76e93d9e55600080da4ca0412000002d3f902d0823a248501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b24206020301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004bb6f4ee179000000000000000000000000000000000000000000000000000004bb8e12eb40000000000000000000000000000000000000000000000000000004bb8e6e78c0000000000000000000000000000000000000000000000000000004bb94b0a5000000000000000000000000000000000000000000000000000000000000000002774a8e4d04db17f63c31246d580f0cd235d5dc3eea4d93de5e403ba1e5e53dd20f5cf9e3962f98819aaec42bbec54ea101b4e9574687d2a31e9d03a0dbcd34e1000000000000000000000000000000000000000000000000000000000000000263b4631075d058beb9465d439635c4ea9f043c534435b3276e213621e8d0acde0d9eb27fdf1a99c44be80437fe52292865d20ae93a1f420f2a74498ca6f7f0d183104ec1a0fb88313af941edda63ba69a5164e5d92ce70bf46c105df81deaaee68ea8c550fa04045c9c1a201989e9dbec697afa90173034cd53eb63e0d575f3d7445553e5074000002d3f902d0823b708501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b24301030201000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004bb778b80c7000000000000000000000000000000000000000000000000000004bb7dd7b2ec000000000000000000000000000000000000000000000000000004bba3d46080000000000000000000000000000000000000000000000000000004bba9c2a060000000000000000000000000000000000000000000000000000000000000000277a27ac96fdee82de316067037886330013dd23a04ed082810852ab30db7f991dde163ae7d9f0754eb3149ec0042c5728700f4ecd962d0cbebb8553cfb98c7fc00000000000000000000000000000000000000000000000000000000000000026efa901d7bbfdd0bc546826d91cb29f038006c0f24fe6c960ad9537e88e2ea0824b4fe023ed4498d7a0f94b5059fe12c31d196cab9f804249ac03d8439b6a68d83104ec2a05c1aee58a55bb0a78161badef33ef860bc375604564bcb23c86d5adb5a960e19a0075be99fc3923194c377d0ccb4477fbc9ffb876397357ef0c957a327b133414400000090f88e83095ecd850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000041f305c0683104ec1a0ebfa984c6ec1c3b45ca4dbf43f07b4907a4d7f2998acf7a29e5bfcf5cc3eb71ea0570c5e83af4639f2049977620efdd75e47b42f4075194d5da253c869446ab4d300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020a71100000000002e08fb0000000065d390e2000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08fc0000000065d390eb000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08fd0000000065d390f4000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08fe0000000065d39109000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e08ff0000000065d39112000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09000000000065d39131000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09010000000065d3914f000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09020000000065d39152000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09030000000065d39155000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e09040000000065d39158000000000000000000000000000000000000000000000000000000000000000000000000009896800003000000000000002e09050000000065d39170000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09060000000065d39182000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09070000000065d3919a000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09080000000065d3919d000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09090000000065d391a9000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e090a0000000065d391b5000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e090b0000000065d391bf000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000090f88e83095ece850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000477a083dd83104ec2a0855dbac8a9cc0b12d1c49cc05249df505f8dba61c6c71c08062db5a796067042a05d2ce5cdddf13ed920353d5cdda4db9bcaeae82a85c9edbb83595dae66ffbd5900000090f88e83095ecf850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000042e9ef9cc83104ec1a0ebcb39ef90fed43e1416826a5df93e4cfc0ed7f2fcc2ea3e6803b36ee74bbad5a033d844e5ef45975575a08edc4635636251416b27c83a9cf7b1a77a2836eba80f00000118f90115028501e36428ca83037f0894ba50f5340fb9f3bd074bd638c9be13ecb36e603d8803311fc80a570000b8a4b2267a7b00000000000000000000000089d4e73c57c37343462f615856ffc94dcb5ab6c600000000000000000000000000000000000000000000000003311fc80a57000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec1a01d78e99d74b32b75fbbe66f458a10c95663206486628ff9aae4ef3ef12efe1c9a027d1f74bae5cb8be2dcdb3ffb48beac93458945e863275477e97ed9a262cd97900000090f88e83095ed0850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000003ecd4dccc83104ec2a0af12c3063a1b8262ec433fa370838b32f8cb4635f137c23f29d1effbffd96b44a021a1d3efbb76179d242fedd170444a809c68f5012df687f1bba22cefe862cfc700000df5f90df2038501e36428ca830c9a178080b90d9c6080604052678ac7230489e8000060025534801561001c57600080fd5b506002546000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610d2b806100716000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce5671461013457806370a082311461015257806395d89b4114610182578063a9059cbb146101a0578063dd62ed3e146101d057610093565b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100e657806323b872dd14610104575b600080fd5b6100a0610200565b6040516100ad9190610a0c565b60405180910390f35b6100d060048036038101906100cb9190610ac7565b610239565b6040516100dd9190610b22565b60405180910390f35b6100ee61032b565b6040516100fb9190610b4c565b60405180910390f35b61011e60048036038101906101199190610b67565b610335565b60405161012b9190610b22565b60405180910390f35b61013c61069b565b6040516101499190610bd6565b60405180910390f35b61016c60048036038101906101679190610bf1565b6106a0565b6040516101799190610b4c565b60405180910390f35b61018a6106e8565b6040516101979190610a0c565b60405180910390f35b6101ba60048036038101906101b59190610ac7565b610721565b6040516101c79190610b22565b60405180910390f35b6101ea60048036038101906101e59190610c1e565b6108f5565b6040516101f79190610b4c565b60405180910390f35b6040518060400160405280600581526020017f6e6f55534400000000000000000000000000000000000000000000000000000081525081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516103199190610b4c565b60405180910390a36001905092915050565b6000600254905090565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482111561038257600080fd5b600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482111561040b57600080fd5b816000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546104559190610c8d565b6000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461051f9190610c8d565b600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546105e99190610cc1565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516106889190610b4c565b60405180910390a3600190509392505050565b600681565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6040518060400160405280600481526020017f6e5553440000000000000000000000000000000000000000000000000000000081525081565b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482111561076e57600080fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546107b89190610c8d565b6000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546108449190610cc1565b6000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516108e39190610b4c565b60405180910390a36001905092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156109b657808201518184015260208101905061099b565b60008484015250505050565b6000601f19601f8301169050919050565b60006109de8261097c565b6109e88185610987565b93506109f8818560208601610998565b610a01816109c2565b840191505092915050565b60006020820190508181036000830152610a2681846109d3565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610a5e82610a33565b9050919050565b610a6e81610a53565b8114610a7957600080fd5b50565b600081359050610a8b81610a65565b92915050565b6000819050919050565b610aa481610a91565b8114610aaf57600080fd5b50565b600081359050610ac181610a9b565b92915050565b60008060408385031215610ade57610add610a2e565b5b6000610aec85828601610a7c565b9250506020610afd85828601610ab2565b9150509250929050565b60008115159050919050565b610b1c81610b07565b82525050565b6000602082019050610b376000830184610b13565b92915050565b610b4681610a91565b82525050565b6000602082019050610b616000830184610b3d565b92915050565b600080600060608486031215610b8057610b7f610a2e565b5b6000610b8e86828701610a7c565b9350506020610b9f86828701610a7c565b9250506040610bb086828701610ab2565b9150509250925092565b600060ff82169050919050565b610bd081610bba565b82525050565b6000602082019050610beb6000830184610bc7565b92915050565b600060208284031215610c0757610c06610a2e565b5b6000610c1584828501610a7c565b91505092915050565b60008060408385031215610c3557610c34610a2e565b5b6000610c4385828601610a7c565b9250506020610c5485828601610a7c565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610c9882610a91565b9150610ca383610a91565b9250828203905081811115610cbb57610cba610c5e565b5b92915050565b6000610ccc82610a91565b9150610cd783610a91565b9250828201905080821115610cef57610cee610c5e565b5b9291505056fea264697066735822122062c5d705a7f338e6087523e87734ea252d47734f9b50b533b2e8833d98b3e77a64736f6c6343000812003383104ec2a039da11a0aaf4f4585f9ce239e5ca1362ddd4e6baf8e8858086770c4ec245630da0572f594ed38c7709a7f01ca8693b626406b4fd490e10edd8a697d5ced072811200000090f88e83095ed1850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000390cf355c83104ec1a08b957dee21bd779ced6fedc21e849ded553375bc3edad91819a0ccbd3f014455a0626d4e53fef18d142bb73745a017ed38cf5eae61b1f928d9901720fb1d8f644500000090f88e83095ed2850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000003500cc67d83104ec1a0a72b9b6c1c173faca6733817681dbbb29fe8aaf8725eb1e2a887d13439756422a01e93e8d5ca31f836000004499595b99f6223849ba598451a82b302d28a016c2a00000070f86e198501e36428ca825208944eaf936c172b5e5511959167e8ab4f7031113ca38719396991e7e3808083104ec2a0580bd119336b27cfeb305cc2a375318ebb6dcd55e4c11cab62320aadeb89a099a06853b9b69e8732ecb402b8242bb8d20a309e656ba7b77df8560dc2e01078a1b100000132f9012f82ace485024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027ec0000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000054a37b00000000000000000000000000000000000000000000000000000000000000001083104ec2a0ad0cc8ea732b871faac022fad998e98b7f9f2feea322319eabe531636e86ac10a01cb516b3812bbfbbe88cf1283a61d39b74f07001c4bd691900b080ba45d3584800000132f9012f82ace585024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028070000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000006553f100000000000000000000000000000000000000000000000000000000000000001083104ec2a01b86d5fa009c2b233d9141d2412b27655bce897af1382d9e895a79ecb19dcbeba05f29793fbf0fa0ff227f11a8702bb0d77c1be7d4b11d12faf813b3053196255e00000132f9012f82ace685024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027b10000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000350f45a2b000000000000000000000000000000000000000000000000000000000000001083104ec2a066b1492627e3ba73a57141cc1ddd064450b5bf64da445ce6f0a44702157060c3a0578a7a36ba5956a0801fcad00eb98d3e7e68cbeb39ef8ba1785560b7e747e2e500000132f9012f82ace785024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027a10000000000000000000000000000000000000000000000000078d000415b3f7a0000000000000000000000000000000000000000000000000000000363d7f800000000000000000000000000000000000000000000000000000000000000001083104ec1a0c56083823fc55bcaf18c11bdcd19809a17c72e2c08e37fe174e5905f68570237a01eb029cc06831e0fcf3994152fb26978268b7413e1680746cb73d2dcbe3e6bc000000132f9012f82ace885024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000280c0000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000000876bf80000000000000000000000000000000000000000000000000000000000000001083104ec2a0b63bda8e47682db7a0dd16c0932fb46c0ba8b012a367603aa5d2997c5c7fd29da02c5d97c1bafa718071c90003dd9bd31c902aef9f751902c850cd3121422d2262000000aef8ac048501e36428ca82cd11949140ef794cc5f702a85f491bd788cc8d2c8bd02c80b844a9059cbb0000000000000000000000005e809a85aa182a9921edd10a4163745bb3e36284000000000000000000000000000000000000000000000000000000000000007c83104ec1a0da291af76d0f9e77406511e7959057368a29f7b99850fdc50cbba81b8178665da05d013eb42de9a0c69d83879fe7b1799d30a5af1dc77a702c011fd5b7cdc5df40000000b1f8af827dea8501e36428ca8301a0f6946f1da9076f36000d5f9decdebd52402b1410b2c580b844468021b70000000000000000000000000996784858e112e5a69b060c6ab8cf0bc7f33652000000000000000000000000000000000000000000000000000000000000000083104ec1a09946ee6431384c96bd4708d6305c07ba426c0383f2c7630d85f6444f20fd84caa05bb2cf575d794fbcaeebff62e65e9f04cbaa7bcb8ba47623e61e2afe9ac0a2a6000000aef8ac058501e36428ca82b6a2949140ef794cc5f702a85f491bd788cc8d2c8bd02c80b844095ea7b30000000000000000000000005e809a85aa182a9921edd10a4163745bb3e362840000000000000000000000000000000000000000000000008ac7230489e7ff8483104ec1a0f058ec1592c640f2c8fb5c1700fc9b530b3c9d1aaaa56e7300ef7e24433bca43a040093739aa4849ae44cf8da0273b6e6680f32bb990d264e090a837797deeba9300000072f87082605e85174876e800825208943156a12eee486fedd3a45d8662855b0bebb72425872386f26fc100008083104ec2a0624f6a4b2a54cc497e3edbc6b2c2626dd889ab3a37c76ebfb94126db3782487fa03b99e63d31bb02f061e63fd6d17424ab8052f0d712fc98d743b090700e8e1d5b00000090f88e83095ed3850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002fd8d6bce83104ec1a0762c90e78b7c38c9b7c2c988979555acc16393c07b1d1d5cfbf521387f4505b6a014d093e11c769695710d5bd14e11c29d2ee48d942c02667cf586b4389551323900000074f872068501e36428ca82ca9094530000000000000000000000000000000000000487470de4df82000084d0e30db083104ec2a09f767be129bdb8c5c9883df21f24e688a539db7d38a9e6c3d7fea1cbec63adfba0379f0a03b0c0096891c029147de7626be24f8bfe3ffcb0754b955f14a77d499000000090f88e83095ed4850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002b70ba0c383104ec2a0afece324e89a2a740b0338974d49a3218beafe132acedb0a9982df313834123ea066e2737f1717122c98ca8fc7c94de6725fe881c847feb20212a52244f82d88c7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fea0b00000000002e090c0000000065d391c2000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e090d0000000065d391d1000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e090e0000000065d391dd000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e090f0000000065d391e0000000000000000000000000000000000000000000000000000000000000000000000000009896800005000400000000002e09100000000065d391f8000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09110000000065d39205000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09120000000065d39223000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09130000000065d39235000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09140000000065d3925c000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09150000000065d3925f000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09160000000065d39280000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000333f903308218e68501e36428ca83081170946fa0a77bb9fc5ac9e9d9c26c101067486291d2b580b902c484d61c9700000000000000000000000000000000000000000000000000000000000000600000000000000000000000007739e567b9626ca241bdc5528343f92f7e59af37000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000001a4c23a4c880000000000000000000000000000000000000000000000000000000000000000a6539ed64a1e6b851183dee68a56343d3652df44b7e7018a75d5b88e1f4231d06f1ce17485fc0c13f979650e2a9c34c9f0813c3731bc5125f588c47048b1bddd0000000000000000000000001e24b33b51e36f3f2391e7e4b51fdab2e7726043000000000000000000000000000000000000000000000000011c74cfea6b0ea2000000000000000000000000530000000000000000000000000000000000000400000000000000000000000041151ceffb743650e14425c7749019e491fd19870000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000000000447647691d000000000000000000000000000000000000000000000000011c74cfea6b0ea20000000000000000000000001e24b33b51e36f3f2391e7e4b51fdab2e7726043000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000419ab8c7ae558df36a642d453f57f39088cad48efcfff2d5fe1c8725ef9cf7c95e405bd74d01392b21b4311b422dc4ad2d8347386a3a02a344512637a835a8994d1c0000000000000000000000000000000000000000000000000000000000000083104ec2a0046df0fd5940ebc236537180a1751c78472209be34c035c8c72d608246a48401a03ba2dde68427ccbe613edc0cfbe23ab2f12403931495114658c53ba45f7fef7700000090f88e83095ed5850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000030dd4ff9883104ec2a041d8a675c1a9b84fb6fe118cf2bc9861175de3f6acdf439f2c8770f80b5c362aa0078ef58d8d765c302b6a959b063f7edb227ac14ebd83c0f89556cf99231beeb400000090f88e83095ed6850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000036f8e253c83104ec1a0a44a7dec24247454511290a89e4276b32e3652f1c7d5080e2ca345c2099cb72aa00bfed34131f0bbe620951d3db82fc9d13da2f7b80505b9218946868b261408c8000000aef8ac078501e36428ca82c0c994d9692f1748afee00face2da35242417dd05a861580b844095ea7b3000000000000000000000000bbad0e891922a8a4a7e9c39d4cc0559117016fec000000000000000000000000000000000000000000000050935671952c6d0ac383104ec2a03851bb4b2b5da75940897f896cd4601dc0e7cfbf388fa0f93cf7ef9a30f0c6d3a061eac6d7f10ecc19f42cfe3e4053eb7a5d72736436d047335164b327f3b3695e000002d8f902d5088501e36428ca8308c96d94bbad0e891922a8a4a7e9c39d4cc0559117016fec8724c116e24162d8b90264ac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000164883164560000000000000000000000005300000000000000000000000000000000000004000000000000000000000000d9692f1748afee00face2da35242417dd05a86150000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000000000000000000000000000000000000001ad9c000000000000000000000000000000000000000000000000000000000001ae8c0000000000000000000000000000000000000000000000000024c116e24162d800000000000000000000000000000000000000000000001a5d8cc3cf4dcc85a00000000000000000000000000000000000000000000000000017242f34b6343300000000000000000000000000000000000000000000000dc4397ad017d92cb4000000000000000000000000853151a55a64182ab87e53629d0a27246b867d2a0000000000000000000000000000000000000000000000000000000065d3930c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a0000000000000000000000000000000000000000000000000000000083104ec1a0ccd8dce6af873bb97e2270326ec41948dcfd3864d52170a27258f96f03a72ad4a07c0d304d9c976b754d3d96e6dc52dbb248a53e9ac091038be7f31bafa69b993500000072f87082605f85174876e80082520894515c3fc211f1709c00034ccd338c2d6f11b0b9ea872386f26fc100008083104ec1a0d8c0b1307695e9a3bbd36da4c31f91d4e7dab387a4b6b09cdd6b431f34fa39b9a0073dd4ec61cc96406d6b9b9537935b900843d4090bd951fc78bb850fc3dd683e00000218f90215098501e36428ca830296d79417afd0263d6909ba1f9a8eac697f76532365fb9587038d7ea4c68000b901a45ae401dc0000000000000000000000000000000000000000000000000000000065d3933100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e404e45aaf0000000000000000000000005300000000000000000000000000000000000004000000000000000000000000d9692f1748afee00face2da35242417dd05a86150000000000000000000000000000000000000000000000000000000000002710000000000000000000000000853151a55a64182ab87e53629d0a27246b867d2a00000000000000000000000000000000000000000000000000038d7ea4c680000000000000000000000000000000000000000000000000034259abcb9f19cfae00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec2a06fb41bc0b7da9c988c793a35010e0f2765cb1b7ca7955322ea69d18aa969cca8a06cd9bf38e19d5405a3993d70742c4ecc536fc8eadc98dabc6c108b3dfcb10ee400000090f88e83095ed7850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000034385d23483104ec1a083544a9e0dbd93ac12386caca57644f9f1c35fa4c4a613bc8e4685b6c0ee2a9da009f3b904fe751bbb869799827f54c968d11a09f6dbe1134610f30bef5dc890ed00000074f872822c6885024411ca8c830f4240947a0222dee3519854468f31154a4349c92814875f88016345785d8a00008083104ec2a06416797680e1adbbaa35f183cc7ddfa2f61d5086dfb34f5f55cad428eb9ba067a037cd782892cbcdbc5240e9b853f39d3efc67c03ff4f82186e481d2bd584a5dfd00000090f88e83095ed8850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000309f3b03883104ec1a0039eba6d285029c490ce4f4ab5a5c503f3cdcb3d4630729286fcbee402ca4559a012e354d003a52f2907e260b0bba6902d5c3cba944fe90dca8d35e5288091f2c500000132f9012f82ace985024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028070000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000059682f00000000000000000000000000000000000000000000000000000000000000001083104ec2a015981104c8999cc643fd5bddd6b4f3b7c00dd46a3fae7ae3f85d8a754910d41ba07e0e5d8f6ea700fda5c3f14cff436dff1b2bca16ae0b0249e6d6fbf8b3173be50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026160b00000000002e09170000000065d39283000000000000000000000000000000000000000000000000000000000000000000000000009896800004000000000000002e09180000000065d39292000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09190000000065d392a5000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e091a0000000065d392b7000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e091b0000000065d392cf000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e091c0000000065d392ea000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e091d0000000065d392ff000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e091e0000000065d39305000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e091f0000000065d3931d000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e09200000000065d39323000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09210000000065d39363000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000132f9012f82acea85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028050000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000000160af0000000000000000000000000000000000000000000000000000000000000001083104ec1a00d687d24a94272e075d31abeb84eacbe5eccce584f0e03d6431b72624c55d5efa01f57327c4edc894fffbbebe7acb9f56b1d5d68b67383d7d4c829bd729771109500000132f9012f82aceb85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028030000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000000001501d8000000000000000000000000000000000000000000000000000000000000001083104ec1a0dca30142d63f63d2ed1811721b4f74877b1c495b889c7aeee07b9fb376a81acba040f5f9b03d494167db7e58973dd4239f3c56e73be36d25a8282baa3ce234c14000000132f9012f82acec85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000280c0000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000007735940000000000000000000000000000000000000000000000000000000000000001083104ec1a0137593bcc6a604ba26e5f964af8f1a357e1c1aa4af4490ee0297dfb9edd8dab5a044c8ebae5a3022c21b6a7b215af48cf73a6246c859c178d197a2ff14e8a1226800000132f9012f82aced85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027e90000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000003732dbfc000000000000000000000000000000000000000000000000000000000000001083104ec2a022422e83c1adfa635ddd967c6d5058772c1bc1bd3651034da5d317a485a1bda9a06413513aaa8c4f0095aa86ac076aea96214fff8772b815a9d3927acaa357f53100000071f86f168501e36428ca825208945e809a85aa182a9921edd10a4163745bb3e362848806507c16dccd93908083104ec2a0ae33688b3298329fba89edadada18cf708eef12156df476e7eef851cb3ea31cba0412719f039131ae36d97550db05b2dcd65cb113426be1f2bf0ff820333c107fc00000090f88e83095ed9850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002c8b368d683104ec2a03030938632d390c973aa69f1793d35f1b227cba1d51d2d776919a3399a0102f7a01b63df2432f77cef2f8f367cdb49362b3cac8b215217ace6195bac4193392f0800000090f88e83095eda850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000296174c9383104ec2a046875e66f3dbf7532900e31022396c0fa79891ff0ce3cce7f5d25a95f6555921a02c09f26a8726251406af0b184d829d4ab3df8b4b9c2f1a8170b2e82b46b410c3000002d3f902d0823b718501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b24b05020300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004bcb025fff0000000000000000000000000000000000000000000000000000004bcc6973d62000000000000000000000000000000000000000000000000000004bd1e426dc0000000000000000000000000000000000000000000000000000004bd2597428000000000000000000000000000000000000000000000000000000000000000025d91fc97e49e7701fdd1f7cc9d7ecbfef4958fd777a78b44cc796b160d2d92b8ce14d922331d558133519ab4a70a04ac054630f21166ec2513431245e499767c000000000000000000000000000000000000000000000000000000000000000264c3c95b18fc44ae5020f0605a40443df9c247512e49edeafaa35a10e265b10051c4636862e76195b6079027b8ffac8fa1cbd24bbc584590b3041b17ed4c4c5283104ec2a01bc5d34f841e5b20e90b35eb1247d8c27244992cfc03da5380481453fdf145a7a01c2020538ca312e3ae909dea6004174249a009be68629e685395802f1e6fc4ce00000070f86e1a8501e36428ca825208944eaf936c172b5e5511959167e8ab4f7031113ca3871550f7dca7253f8083104ec1a0cf32c0417d23037bd5fc134eef92e7016c1625a9c26eee13ae70747c633dfd9ea030291e772795a0e9e732e060fff814350d1ffa5e7b7eb6d2f785ce36edfa73d900000090f88e83095edb850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000026227159483104ec1a0988674d62b0c2e8ec91a03005299b26764a96f2b10e1ff6c8079c8e3d276e394a00165f11fae4ecc3cf9f3136b1a797773a113d35ab3e216d261f871ae25d7d98000001453f9145082028a8501e36428ca83039bf894e432150cce91c13a887f7d836923d5597add8e3180b913e409c5eabe000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000013a0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000008274f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000019be5baf8dfc9e65d967d7799789b77fb17869dd823628c8eb799544ad9b74552000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013617070726f7665436f6e747261637443616c6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000d294975a1e0994ad2d835c9431ad9cb131fffde6a32fc7a459b896c3fe2631f73dfba019a171a60982bb86bf719fceb18e0f4a40b44a576f276f6bd9c4d987d5357415ffda9d36683ca0b066f2abec52a24b5ef8000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000094176616c616e6368650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078386137373135663135413439446634623439346330444630333138393965434142326633314235350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000067600000000000000000000000000000000000000000000000000000000000000d800000000000000000000000000000000000000000000000000000000000000033000000000000000000000000033249f556213e290f64cebcca0f4ae59c2d62cc000000000000000000000000081bcea66fe8ca5e09a2d4425103a7804c6c1e270000000000000000000000000b6f38dae6acb217a07bf15f8fe8321537c83f43000000000000000000000000110e75163d776f0005e1448e7dba592ba21ca78300000000000000000000000011fdde73fb55ed8a8bba92f3e9fcb762fa6ba4fe000000000000000000000000190d382a335eb4b0f131324299ff8225c9ae911d0000000000000000000000001e31a5efedc34abd89449e88bb003f26e6decf03000000000000000000000000206b1b84f594df59b9cf23cea94be1178c2048f4000000000000000000000000219cc470e99b70d9111dcfa025ce9a1ea38ed507000000000000000000000000239eb449a60cd10eb265c413e62282820362eea000000000000000000000000023c6efebc5b4d1cc38fc74a4ea96de1703d387d400000000000000000000000027c28c665e25cd8023f442dbcc8abab87bb2538300000000000000000000000027cda603b1ae1b1cb1b4cfba6351866f374262910000000000000000000000002d00ca80a286528cc8611db5b9263f0fb6e3504600000000000000000000000038be7812944137d3dc2b78281ea0eb55a421293b00000000000000000000000048b4c3be622660924cdc64667e14cdd8ce348ddb000000000000000000000000493254f114efe1eed187e5f8d7dd7a367f5024f9000000000000000000000000537518a5ba6c3b506d9c2214cb518beaae867bf70000000000000000000000005d18e78132aedf86f2a405d8d2933dc204a73be90000000000000000000000005fe789148085683fac0dcce989bfb807271e8d580000000000000000000000006102c805a85e14853e1e3cfb5483304c6d08c59d00000000000000000000000065bfa0ddf26e90545a4fcdbdc4d7b7eee735b82200000000000000000000000068a4fb59f7f3022ccc18752f98f70e04d6d16ae60000000000000000000000006e23f8928fde6ce4b60d95f224be452538beafdf0000000000000000000000006fb12e213b0ea19cbb31ab307077acd591061ca200000000000000000000000078e79d001165136c07b85d4ec945a28b8552b1a20000000000000000000000007a3bf5cab8492d57ec310524430f77b96d0502580000000000000000000000007b68b88f9f8751ddd3692e83a230e0228133f9e2000000000000000000000000819c4c684ac7a1f3bfbaa556535851e2e82e2e650000000000000000000000008298182a159be69b96e5ebf94faef1bacb6501410000000000000000000000008c30107fdf21c3ec344b018190a7c22377536aff0000000000000000000000008f5de99cdb8795fd8c60dded53541ea9bf4c8a790000000000000000000000009218f827015c3191d2c61faf38c2fd64c1491e5d0000000000000000000000009790b9b287c51278b00fdbab2715b01e01d2c8210000000000000000000000009b2f17f65195a904a783bfdc1c40c306d643aa18000000000000000000000000a2d9f6b5893f7427af8978728c38d16b681a2824000000000000000000000000a41090021ba7833c2e34dd25650a191d720e4fbe000000000000000000000000aac7332380cce90e64a0b66f7d5c045b81573141000000000000000000000000abe6af325077d6041047beff59dc4ae71488c600000000000000000000000000b55bb609245d7113d610f29720e90740c6649f52000000000000000000000000bf971c96714c5198ad8a8ca001ef1c5eed790c6c000000000000000000000000c30836594698699f8f0107e35f98da6040ddf436000000000000000000000000cde01607a0d73d9ec92c6a0894684797e34b5829000000000000000000000000da664a27915cd9cbbd44100ed4276b6c4eadcec3000000000000000000000000df7468a4e4e87774d8678e864159d0f782024d2f000000000000000000000000eccedd1eef19a3efd143973a8cbfec3c590b42ea000000000000000000000000f06f2a8de1bf5a053b0b61434b57744a07896ee8000000000000000000000000f100598e3c48018c54d7d7107109f2be5330323e000000000000000000000000f50829c33d1b9c3af30816167637de6a290249ee000000000000000000000000f9efca275a19f8a397051eb4e6cf82626d9b68f9000000000000000000000000ff830d66181e909399ac67122db81588265df026000000000000000000000000000000000000000000000000000000000000003300000000000000000000000000000000000000000000000000000000000003b5000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000008d00000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000016900000000000000000000000000000000000000000000000000000000000000db00000000000000000000000000000000000000000000000000000000000001d50000000000000000000000000000000000000000000000000000000000002275000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000222200000000000000000000000000000000000000000000000000000000000021af000000000000000000000000000000000000000000000000000000000000023500000000000000000000000000000000000000000000000000000000000000ab00000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000000cd000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000000980000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000031300000000000000000000000000000000000000000000000000000000000000f400000000000000000000000000000000000000000000000000000000000000e70000000000000000000000000000000000000000000000000000000000000066000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000b5000000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000013e000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002d7000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000001cc0000000000000000000000000000000000000000000000000000000000000091000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000008d00000000000000000000000000000000000000000000000000000000000001c40000000000000000000000000000000000000000000000000000000000000098000000000000000000000000000000000000000000000000000000000000009500000000000000000000000000000000000000000000000000000000000000ad000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004104992b5b57d0b592602250565baf2cfabfc02bd7fd5a8a98532132ea53baadb832536c54c6b1daa2c25d3673f85b4807006384eb0d3a828b1ed2ab2e2d4318bb1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004158f6593275e5bf500298f9a19407ffce2a3276bd17f83a328e47a605d9ea08a36d735c885cdf7d1eea30db7af4f391b8a0b3f8c454f4ab1bf935a026bca7facc1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000418bd4955c63f4f29673a44360e9d4bccff341ac2950d21dbd8868b6e814492eba76b8f6a2408feabfe2c640b7a50ae00618574dabaf38cf5a18d1e9240c45508c1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f490dca93d8822b040602430ea258070e3d7d5bc501d8cf1907a874b20937dea04220465e5aa2475bff3d9510741e2058e7c83fddffe3d9377a74c093dda02791c0000000000000000000000000000000000000000000000000000000000000083104ec1a0cae6b0ae80b98e807cc88b61980f8ab3ea7c54eefd2ff55e81969d9261999ecea02aa37e136ed385f34880c989ed5dbc71307d6775f273486f395967f3a14bd0fe00000071f86f0b8501e36428ca82520894966958570fec2a6da8d634b1aa25db9a606bf1628802c68af0bb1400008083104ec1a0c7d85970f044135390e0ac2cba0b6088b0a3ffb6aeaeab9fc4906d8101626161a05ecf122fe6d7354f303a57a7a905928e6182005a0d878b333dda914342f0248f00000072f8708222ed8501e36428ca82520894a6296f35370b9166263148cbd750dc4f25f455d587038d7ea4c680008083104ec2a02c8b4bc81498d1e82f791053bb5e50daf5e33a7ef74a714742a203e4b7ad90efa07b9817d21eadacd8ffcf6bff17eee490db08b9bafb493fd5f7c8b37735d304cd00000353f903508202878501e36428ca8301668694d294975a1e0994ad2d835c9431ad9cb131fffde680b902e4491606589be5baf8dfc9e65d967d7799789b77fb17869dd823628c8eb799544ad9b74552000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000094176616c616e6368650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078386137373135663135413439446634623439346330444630333138393965434142326633314235350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000001e3062bebedd20000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000014ebb18bb1aba7b1f7d496144c96e9171c89caf5ad00000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001487243a1ea236745296ab9a0a736d594de956b32c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec2a09f0318214ea3c7504afa9f4aa2c067497c8ef67eef19078ae672fbf39f657f3fa003568a32a67936a2b0727e945f3ec2e2a0f9879fa6b19e72ad69699d3d59001c00000090f88e83095edc850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000024259e97983104ec1a048dbfaf73f8946d2018773a2f0cffd4cbf956ccaad8846d0ae2ffe8c6e1365f2a013e7d6a41b74d2f2ed7c87142f84f6940d8e73b26b06f7765e3ab505cc8bba2c00000000000000000000000000000000000000000000000000000000000000000000000000000000297f0900000000002e09220000000065d39369000000000000000000000000000000000000000000000000000000000000000000000000009896800003000300000000002e09230000000065d39375000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09240000000065d39381000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09250000000065d3938a000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09260000000065d3938d000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e09270000000065d393a9000000000000000000000000000000000000000000000000000000000000000000000000009896800006000000000000002e09280000000065d393af000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09290000000065d393d9000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e092a0000000065d393ee0000000000000000000000000000000000000000000000000000000000000000000000000098968000010000000002d3f902d0823b898501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b24e02010003020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004be3c2ed700000000000000000000000000000000000000000000000000000004be6d5feb20000000000000000000000000000000000000000000000000000004be6d5feb20000000000000000000000000000000000000000000000000000004be6e3e9ea10000000000000000000000000000000000000000000000000000000000000002d3a0495f1ecca506db79fbcddd8e7de2aad46482561eece314f23668f0d139c98264053151b3b6adfd8ff4a75b5ed34d2aec9bb2916e32320c341126c99767a200000000000000000000000000000000000000000000000000000000000000021e436313ceecc97f708c9432a41e6a38fefc196e090867f4ba02d67c649583683975459219a847cee89c3c4e58cc48a3dfbfba88b01c2466a2c7920f54a7e95283104ec1a0faf41deab9646e17598b653e4c95ba1b927458f0144b73f5137ef14ab812cd78a07f408d8991042ca5d642fb911c9b0d22cf88fc3f23039e6e5d6461efcb358c0900000090f88e83095edd850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000021ad88f5183104ec2a0dabe37b009fbb92100b39f75f6cc183f155ad9b1277205463587e0fbabee1569a024da9422a0e83faa7def9fa613f7dddbf5f989ebb634e5f68aea3bd1ddbf222d000000b1f8af827deb8501e36428ca8301a0f6946f1da9076f36000d5f9decdebd52402b1410b2c580b844468021b7000000000000000000000000b89fb651642a8c8446c760732ffc25c2c9f190f4000000000000000000000000000000000000000000000000000000000000000083104ec1a0b3ec1f069ce335a430650443a4c35a3b7a2d5daf83a0c305420ed592d9eb17f5a07c3357e7b6e67d1d7c3e56527e4b9f296d1503a75563aa1c4c4bd080fcdd1b8c000000b1f8af8207b385024820220083010f0594ea1e8e805b8671a4ec80735d5b715c551a21850d80b844095ea7b3000000000000000000000000077b28fa1a537780b372cb5bb22aa7d8d30e22d5800000000000000000000000000000000000000000000000000000000000000083104ec1a00c9c8a44357b1c545d34e4c961a3bc3f1ae6b07a6fdcd1032c8365b89186d405a06059daf66df98ef8a15bd3bafd57f474d378d1dd924d70c00713e2841dc9c0db00001453f9145082028b8501e36428ca83039bdf94e432150cce91c13a887f7d836923d5597add8e3180b913e409c5eabe000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000013a0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000008274f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001f3298e326db32aa16b18ea20628aa4704c8eda7011ba6c922cf47e68196c38b1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013617070726f7665436f6e747261637443616c6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000d294975a1e0994ad2d835c9431ad9cb131fffde6139dad6c2e3d0679f411463c7dbc0eb99db34655f4f9facf698e700f3860be2080ad37add9b944dd8eade01b5b9c8648b36c709332ca320a98a67600eb7bc881000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000094176616c616e6368650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078386137373135663135413439446634623439346330444630333138393965434142326633314235350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000067600000000000000000000000000000000000000000000000000000000000000d800000000000000000000000000000000000000000000000000000000000000033000000000000000000000000033249f556213e290f64cebcca0f4ae59c2d62cc000000000000000000000000081bcea66fe8ca5e09a2d4425103a7804c6c1e270000000000000000000000000b6f38dae6acb217a07bf15f8fe8321537c83f43000000000000000000000000110e75163d776f0005e1448e7dba592ba21ca78300000000000000000000000011fdde73fb55ed8a8bba92f3e9fcb762fa6ba4fe000000000000000000000000190d382a335eb4b0f131324299ff8225c9ae911d0000000000000000000000001e31a5efedc34abd89449e88bb003f26e6decf03000000000000000000000000206b1b84f594df59b9cf23cea94be1178c2048f4000000000000000000000000219cc470e99b70d9111dcfa025ce9a1ea38ed507000000000000000000000000239eb449a60cd10eb265c413e62282820362eea000000000000000000000000023c6efebc5b4d1cc38fc74a4ea96de1703d387d400000000000000000000000027c28c665e25cd8023f442dbcc8abab87bb2538300000000000000000000000027cda603b1ae1b1cb1b4cfba6351866f374262910000000000000000000000002d00ca80a286528cc8611db5b9263f0fb6e3504600000000000000000000000038be7812944137d3dc2b78281ea0eb55a421293b00000000000000000000000048b4c3be622660924cdc64667e14cdd8ce348ddb000000000000000000000000493254f114efe1eed187e5f8d7dd7a367f5024f9000000000000000000000000537518a5ba6c3b506d9c2214cb518beaae867bf70000000000000000000000005d18e78132aedf86f2a405d8d2933dc204a73be90000000000000000000000005fe789148085683fac0dcce989bfb807271e8d580000000000000000000000006102c805a85e14853e1e3cfb5483304c6d08c59d00000000000000000000000065bfa0ddf26e90545a4fcdbdc4d7b7eee735b82200000000000000000000000068a4fb59f7f3022ccc18752f98f70e04d6d16ae60000000000000000000000006e23f8928fde6ce4b60d95f224be452538beafdf0000000000000000000000006fb12e213b0ea19cbb31ab307077acd591061ca200000000000000000000000078e79d001165136c07b85d4ec945a28b8552b1a20000000000000000000000007a3bf5cab8492d57ec310524430f77b96d0502580000000000000000000000007b68b88f9f8751ddd3692e83a230e0228133f9e2000000000000000000000000819c4c684ac7a1f3bfbaa556535851e2e82e2e650000000000000000000000008298182a159be69b96e5ebf94faef1bacb6501410000000000000000000000008c30107fdf21c3ec344b018190a7c22377536aff0000000000000000000000008f5de99cdb8795fd8c60dded53541ea9bf4c8a790000000000000000000000009218f827015c3191d2c61faf38c2fd64c1491e5d0000000000000000000000009790b9b287c51278b00fdbab2715b01e01d2c8210000000000000000000000009b2f17f65195a904a783bfdc1c40c306d643aa18000000000000000000000000a2d9f6b5893f7427af8978728c38d16b681a2824000000000000000000000000a41090021ba7833c2e34dd25650a191d720e4fbe000000000000000000000000aac7332380cce90e64a0b66f7d5c045b81573141000000000000000000000000abe6af325077d6041047beff59dc4ae71488c600000000000000000000000000b55bb609245d7113d610f29720e90740c6649f52000000000000000000000000bf971c96714c5198ad8a8ca001ef1c5eed790c6c000000000000000000000000c30836594698699f8f0107e35f98da6040ddf436000000000000000000000000cde01607a0d73d9ec92c6a0894684797e34b5829000000000000000000000000da664a27915cd9cbbd44100ed4276b6c4eadcec3000000000000000000000000df7468a4e4e87774d8678e864159d0f782024d2f000000000000000000000000eccedd1eef19a3efd143973a8cbfec3c590b42ea000000000000000000000000f06f2a8de1bf5a053b0b61434b57744a07896ee8000000000000000000000000f100598e3c48018c54d7d7107109f2be5330323e000000000000000000000000f50829c33d1b9c3af30816167637de6a290249ee000000000000000000000000f9efca275a19f8a397051eb4e6cf82626d9b68f9000000000000000000000000ff830d66181e909399ac67122db81588265df026000000000000000000000000000000000000000000000000000000000000003300000000000000000000000000000000000000000000000000000000000003b5000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000008d00000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000016900000000000000000000000000000000000000000000000000000000000000db00000000000000000000000000000000000000000000000000000000000001d50000000000000000000000000000000000000000000000000000000000002275000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000222200000000000000000000000000000000000000000000000000000000000021af000000000000000000000000000000000000000000000000000000000000023500000000000000000000000000000000000000000000000000000000000000ab00000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000000cd000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000000980000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000031300000000000000000000000000000000000000000000000000000000000000f400000000000000000000000000000000000000000000000000000000000000e70000000000000000000000000000000000000000000000000000000000000066000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000b5000000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000013e000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002d7000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000001cc0000000000000000000000000000000000000000000000000000000000000091000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000008d00000000000000000000000000000000000000000000000000000000000001c40000000000000000000000000000000000000000000000000000000000000098000000000000000000000000000000000000000000000000000000000000009500000000000000000000000000000000000000000000000000000000000000ad00000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000419e4cdd838f099eda8cbfbc48047259e9ce9edd5bdf1a7111aedde46fdb752e501b24052ce318b72beb033fc8ebffaccb86d653e16adc26f83f34a76e71bc66d71b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004150b36bbe0617ba18b6240e475bc5169e5b34dae65b06bb3c9f62f4d0a2c18ef227ad3341d5b0903d4cf5034396c229829e061a47e2294a7c456250519e57c26d1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041ebbe62e7c8a270f28f62ad0840c6caf25752b7cc609e4499612f048e3fcc47ad4fbee4b0346ab8a5541343295629e28ce1d27ae0078b086fc10123e858424d481c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041b6a252794e58fd0676a75a0c4fbb8c3330e17c09cf87ce7cc22a478c052675890ae83ffe966b7dfb9e99016ec19ea41c9bad8a6801c1a2f2480d76e39f384e721b0000000000000000000000000000000000000000000000000000000000000083104ec2a0012aa28041ad09da4313d27af2aa2fe2751778bf61f19ea977048be7c89313f7a07ad5de5195b4f25cce9bf4e15394c20e9509ff4437513309a6c68575662eaab400000132f9012f82acee85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000280c0000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000000056e1360000000000000000000000000000000000000000000000000000000000000001083104ec1a0c3d4481a44fb67ca969e3c70594dd64dbccefa9fa09dbb438490dbebdef88e5aa03037f0e555751d7ab5e8db3a5a333f3c6d35cd0e172df29344d0f857c5c9e3b300000132f9012f82acef85024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027ec0000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000000364cc1c0000000000000000000000000000000000000000000000000000000000000001083104ec2a071c4eccbf350cc3a9babe71acf438a6a370ee4332408bcd7886a618cd9a37cd9a034a2404be847b76e9db228d850b35677156bb053ff3f5fea719894a7a33a5ad800000132f9012f82acf085024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028070000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000040f81480000000000000000000000000000000000000000000000000000000000000001083104ec1a0f3acb5c4695d3eedbedf721086e87097ea7633f3f2bfe41b8727d7823802ad9fa012c6f64f110b3deda0a7fe5388616748def78ef73a292a506098fee918ab029400000132f9012f82acf185024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027b10000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000234066254000000000000000000000000000000000000000000000000000000000000001083104ec1a0e54e9334cb39d48bdadff220182021ef84fc6e061237f493e5b66bc7b8848942a03b478cb63c175d39c71f31b7e1793eaeae8171d8bbd2ae02f5ae00ef69b21ea100000132f9012f82acf285024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028030000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000000000f478e000000000000000000000000000000000000000000000000000000000000001083104ec1a03294e3b34512799cd02e594a1e0ae20e960ce3e458728c40e9ae52f3defe7768a050f89c9ae3298968b81a3ba59a4cb5a70784c673e4727154e68d4d96c63a3d6800000132f9012f82acf385024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028050000000000000000000000000000000000000000000000056bc75e2d6310000100000000000000000000000000000000000000000000000000000000002ab802000000000000000000000000000000000000000000000000000000000000001083104ec1a02e8db9d20e7f34606a44cca663cecdbe3562a9f47d7a28e60d8df20c4ad7a374a07ba87d7cb343d2ac9ad260a24c2fc16a6d01a4fddafa0dd4ffb7486df713bb8a00000353f903508202888501e36428ca8301668694d294975a1e0994ad2d835c9431ad9cb131fffde680b902e449160658f3298e326db32aa16b18ea20628aa4704c8eda7011ba6c922cf47e68196c38b1000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000094176616c616e6368650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078386137373135663135413439446634623439346330444630333138393965434142326633314235350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000001dd81f3305d760000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000014ebb18bb1aba7b1f7d496144c96e9171c89caf5ad00000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001487243a1ea236745296ab9a0a736d594de956b32c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec1a044b6a8a09ef25279c8b802b2c4d1c7ce6a69583e39b73788cf24f32bb8f64485a04752107e5b2ca457319292082748a323b4ecd3a9b38b3b2d0ef1629f668a674900000323f90320808501e36428ca83031f13808502540be400b902c560806040526040516102a53803806102a58339810160408190526100229161009b565b8042106100815760405162461bcd60e51b815260206004820152602360248201527f556e6c6f636b2074696d652073686f756c6420626520696e207468652066757460448201526275726560e81b606482015260840160405180910390fd5b600055600180546001600160a01b031916331790556100b4565b6000602082840312156100ad57600080fd5b5051919050565b6101e2806100c36000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063251c1aa3146100465780633ccfd60b146100625780638da5cb5b1461006c575b600080fd5b61004f60005481565b6040519081526020015b60405180910390f35b61006a610097565b005b60015461007f906001600160a01b031681565b6040516001600160a01b039091168152602001610059565b6000544210156100e75760405162461bcd60e51b8152602060048201526016602482015275165bdd4818d85b89dd081dda5d1a191c985dc81e595d60521b60448201526064015b60405180910390fd5b6001546001600160a01b031633146101385760405162461bcd60e51b81526020600482015260146024820152732cb7ba9030b932b713ba103a34329037bbb732b960611b60448201526064016100de565b604080514781524260208201527fbf2ed60bd5b5965d685680c01195c9514e4382e28e3a5a2d2d5244bf59411b93910160405180910390a16001546040516001600160a01b03909116904780156108fc02916000818181858888f193505050501580156101a9573d6000803e3d6000fd5b5056fea264697066735822122039646d3a68203fdb441a3414dadab989dc84877d7690079e5b922deb0f014e7264736f6c634300080900330000000000000000000000000000000000000000000000000000000067b4c75683104ec2a074f5a4f4415e8b1b294a1466a2652461450dc8a6e02a5aaaf8ea66f4f97de6c8a021b517744b1332ce01849316c35912c2edcc430a435d3dbc25f1b4ac8d2c5df200000070f86e018501e36428ca825208945e809a85aa182a9921edd10a4163745bb3e3628487271471148793a38083104ec2a0b38ba2e6650fe7493240c861ef3c07411584cbc8eed3915997204cc96771bd3aa07c4168298109130110b0c8d7ccb85ef0f1cad3c098f4a8eaf78149b07d81e994000000000000000000000000000000000000000000000000000000000000000f521000000000002e092b0000000065d3940c000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e092c0000000065d3941b000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e092d0000000065d39424000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e092e0000000065d39433000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e092f0000000065d39470000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09300000000065d39485000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09310000000065d394a0000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09320000000065d394a3000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09330000000065d394ac000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09340000000065d394b2000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09350000000065d394b5000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09360000000065d394c4000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09370000000065d394ca000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09380000000065d394d3000000000000000000000000000000000000000000000000000000000000000000000000009896800002000000000000002e09390000000065d394d7000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e093a0000000065d394e3000000000000000000000000000000000000000000000000000000000000000000000000009896800002000200000090f88e83095ede850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002364dd4ff83104ec2a0f96c20c4f1c7975271d265c8cf3297a3135c8fab9f31391d181672cba1eeb808a06d89c68f3937f5864c735d038ae9fcd62bf3ffd5df0b63cb7bdba849ab65f6cd00000072f8708207738503c6c851948252089473f9b3f48ebc96ac55cb76c11053b068669a8a67872386f26fc100008083104ec2a0ff0bc97f187af1a4a269c64f0800861d29c8aa20ab43b5114ca86ca6bdc20d1da06cd73f748589454f748223c2d91311476b024f44c86d35811cbc2a6bfb0a0909000000b1f8af8207b485024820220083010f05944731b9205e0b160f471aa856ea4ea1b7d1d2d23d80b844095ea7b3000000000000000000000000077b28fa1a537780b372cb5bb22aa7d8d30e22d5800000000000000000000000000000000000000000000000000000000000000083104ec1a0e74d72b461fc68865c152c0c2ccf12160d9e27f78e1d35b4abbaa23591f28562a07ca7202d7f78adb93462e0db2b79272f084f9c510ff3f80931a23eeefe46533c00000090f88e83095edf850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002545b1b4f83104ec1a02ba0f0b59e005e4db7355b2d4656f991e5c373ed4d335f8a804b72538ce138fca05103175d26b40dc267195b4f9f6ef9f588fd6155f0154cf3235b0758f40788c300000090f88e83095ee0850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000273bd1d2f83104ec2a0b346942c832035b2a5104d0f23b4286cf294f414970391bdf8a7b5d67ca768efa04f6705af092c0250d77df1cf4ea30ebaa6736ea7deec9316fec79865e93fbaeb00000090f88e83095ee1850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000249d7f47a83104ec2a036408fc644a1212b8d482c9223bc41aa0614fc6bbc56f7c78c220691857e83dba00aad307abbe12b963065851fc1f3ad946cf56aca34410f3391880fc2ad39f8ed00000070f86e028501e36428ca825208945e809a85aa182a9921edd10a4163745bb3e3628487354a6ba7a193908083104ec2a07f47cfe91c1338b49050de0597ee9338fe0ff78792f015bb95d1b96c7c2e0aa2a00ad044333d6615e63619759e49865ef4a052eecf5a0d8f8b8f6bbe9ffebe66d700000073f8718227bb85024411ca8c830186a0946a65ae9cc23dbf7179b91efcd205797f2353669087038d7ea4c680008083104ec1a0aeeefe22d92b0b48a310449190efcf457fedaf3d226637199961510e73019cada064cc978c9651dc505d80f465e2ba836a3b8f9c0f380c3e666143bde997c019b200000090f88e83095ee2850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000227e102a483104ec1a0aa03ef2ee0a231d28a4495c31d5b77160bf76ce95d1c293c98cc836eb4527d94a05738e80c60427766a7feb77575be7f8b0d7229acdc4965a5143779d0f2ddf1ca00000071f86f758501e36428ca825208945e809a85aa182a9921edd10a4163745bb3e362848804ed369e7f4393908083104ec2a0814789347708de276f14333e2bea5dffda8095c5c0ad3c65ff184c189761cd72a0304bdd85fe0d97e2e663d4b0a68ea483eca7db33380045c690c6c7073585193c00000152f9014f8207b585024820220083036acb94077b28fa1a537780b372cb5bb22aa7d8d30e22d580b8e4baa2abde000000000000000000000000c863a642d2702d16f819696bf14eb8a2268a899400000000000000000000000075f69882aba1bbb2a75b5b05db3f00c5f365090c00000000000000000000000000000000000000000000000000038d7ea4c68000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007dbc67df4d4ea21420b1baa077028d2c1cca73990000000000000000000000000000000000000000000000000000000065d39bb183104ec2a025a1d9ce46389a5f06a7946b16c45255d16942e949c31f849f561982e8681395a01a208e8450162b020529fd51cc3d7d2e7ef702c48a16f1e81b224266427e4a9900000072f87082606085174876e80082520894b9946c6b9d3cfc3b5eeab732dc8eb6e6dd0b6a03872386f26fc100008083104ec1a07473f8b7fc259ec38738801b358e53d7bd2250cafd00a055aab450ebe032dc3aa020981a0996cf3ce43e6baf484b0a58f493b2dc624b06ea436ec3602da967b24b00000090f88e83095ee3850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000250501cc983104ec2a08d5dda9e5bdea760e5784f33142451cf3b5452febe0adef58490d64ec4707433a05b5164add25bf9ee6464254a30bcd4660daaf622e4b3d2a84f1f81023d65e44000000152f9014f8207b685024820220083036ae894077b28fa1a537780b372cb5bb22aa7d8d30e22d580b8e4baa2abde000000000000000000000000f4d453bdf9f41457c40887b16c8341e859cd408100000000000000000000000075f69882aba1bbb2a75b5b05db3f00c5f365090c000000000000000000000000000000000000000000000000000000e8d4a51000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007dbc67df4d4ea21420b1baa077028d2c1cca73990000000000000000000000000000000000000000000000000000000065d39bd383104ec2a0aed4c52d1b0993df09faff1cd0d9b232ae397787a0ed5718e3aedf223b2ba247a046a863fef3e3ee656fa9b7fcaae9a212a26e1f1b18aa5a652613ddb7cede609900000132f9012f82acf485024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028050000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000000749b8a000000000000000000000000000000000000000000000000000000000000001083104ec2a0d39fd8030f2c0c88ab7485e8cad9cc8e48d769ccd2206630c8c00c4fc97a7a9da0469a847cdebd9860b8aeadac49bcfe3c020be4ac99e25005838b8ff76527bfca00000132f9012f82acf585024411ca8c83035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027e90000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000003f1641cd000000000000000000000000000000000000000000000000000000000000001083104ec1a0b7c90e1b6912750d09cd18a208a0dbded04c0705d2ca5bd8f06b7172cf3a545da066fbec5f0bbab8754be0d05a23b040ade1ba11639b9879b444b88ff1d740228900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d8f0800000000002e093b0000000065d394e6000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e093c0000000065d394f2000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e093d0000000065d394fe000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e093e0000000065d39504000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e093f0000000065d3951a000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09400000000065d39523000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09410000000065d3954d000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09420000000065d3955f000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000152f9014f8207b785024820220083036afb94077b28fa1a537780b372cb5bb22aa7d8d30e22d580b8e4baa2abde000000000000000000000000c863a642d2702d16f819696bf14eb8a2268a899400000000000000000000000075f69882aba1bbb2a75b5b05db3f00c5f365090c000000000000000000000000000000000000000000000000004ce73bf7d0e8c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007dbc67df4d4ea21420b1baa077028d2c1cca73990000000000000000000000000000000000000000000000000000000065d39be483104ec1a0fb3e5956d4474e863317399a7fa26a1a28087e6f6921fa93fecdec100527aaa4a0447112aef5f0536cfda6e1dd87457cd4d074d11aa5308f5b189dbe925c98758100000090f88e83095ee4850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002310f418c83104ec1a0f2aca0c235e8e3b0180c12c041a5703c9ec820e8bcbe7d63fc09197cb010d85da01d459b03ff71a3a6ab64dec7dab46d1c7256c8aa8464dd5e1067e6150ad26d3000000433f904308207be85025c3d32fc830282b094ca11bde05977b3631167028862be2a173976ca1180b903c482ad56cb00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000a38c2b5408eb1dceedbec5d61bed580589c6e71700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010400000082000000000000000000000000000000000000000000000005457808eddbd4bec30000000000000000000000000000000000000000000000000000000065d3946c0000000000000000000000000000000000000000000000000000000000000060456f052eb9cae61f400ae928802b97171711c872fd5aededd29ef77e459cfb22000000000000000000000000c5ae7423f41927d6c31587995482030190d2c807000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000030c5c75000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d20f1ec72ba46b6126f96c5a91b6d3372242ce9800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000010400000082000000000000000000000000000000000000000000000000347a7d198c6262190000000000000000000000000000000000000000000000000000000065d3945800000000000000000000000000000000000000000000000000000000000000603bafeb5195b8d500bc8dedd41daedabfc61399c8beded77c3e610f38c22f6db30000000000000000000000007b3be6648535fe990956ca64e243b6ac1e668c10000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000035cc5d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec1a07567c604801d4d637b7008cbd1c351ea88350e9d807af0e404342960179da6eba03dcc11f899788c68a1c6e41a75811b93fc5671fe6c31782e3de2521b9448a74500000173f901708207b88502482022008303238194077b28fa1a537780b372cb5bb22aa7d8d30e22d580b90104e8e33700000000000000000000000000fd9b4bb0e25031ad12029e66d17c4de826a0d3a300000000000000000000000075f69882aba1bbb2a75b5b05db3f00c5f365090c00000000000000000000000000000000000000000000000005194bc419864840000000000000000000000000000000000000000000000000000000041f461bcd0000000000000000000000000000000000000000000000000512c4fcb81dee800000000000000000000000000000000000000000000000000000000419ff5baa0000000000000000000000007dbc67df4d4ea21420b1baa077028d2c1cca73990000000000000000000000000000000000000000000000000000000065d39c0583104ec1a084c420ed50ea84721dd6103d3ac50ebc7ccb620adafd4596e9e390c885c1d542a00ac572a29e7d65731952a70bf973f9b3d524546f7e11797ef1baaf32b7e8431300000173f901708207b98502482022008303237594077b28fa1a537780b372cb5bb22aa7d8d30e22d580b90104e8e33700000000000000000000000000f4d453bdf9f41457c40887b16c8341e859cd408100000000000000000000000075f69882aba1bbb2a75b5b05db3f00c5f365090c00000000000000000000000000000000000000000000000002e73d20c9f0648000000000000000000000000000000000000000000000000000000001ccb64c8a00000000000000000000000000000000000000000000000002e385c84e0ca18000000000000000000000000000000000000000000000000000000001ca6896650000000000000000000000007dbc67df4d4ea21420b1baa077028d2c1cca73990000000000000000000000000000000000000000000000000000000065d39c1983104ec2a0eeb4c93cf7204805c0061753d0d1b66aa5dac7a3339dcd13a327621f8fa1d75ea00403ede9fec1155fe34bffd7e2858070688f3d20e0189861a2cd14d54fafd23c00000090f88e83095ee5850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000020a3b08c183104ec2a0c1840fe6a87db5c0e74a98486e438794232b04288564a53ff740cd43f10f854ca05f5f28ca51fa0d39144f009a57b4470a414839f1c20f6d892ab87f886009150900000173f901708207ba8502482022008303228894077b28fa1a537780b372cb5bb22aa7d8d30e22d580b90104e8e33700000000000000000000000000c863a642d2702d16f819696bf14eb8a2268a899400000000000000000000000075f69882aba1bbb2a75b5b05db3f00c5f365090c00000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000000000000000000000000000000000000022120a4200000000000000000000000000000000000000000000021b63fd1aa400c000000000000000000000000000000000000000000000000000000000000021e66e0c0000000000000000000000007dbc67df4d4ea21420b1baa077028d2c1cca73990000000000000000000000000000000000000000000000000000000065d39c4c83104ec1a06a8c479dd4afac89651e81d1ba9f53f6de4742689bf456c8de04c82e5493f1afa0718fec692f3107109d4876fcac4bd3dc1e78eb7b3c85e54d36de1a11442096ad00000090f88e83095ee6850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000001dbf2391783104ec1a043575d861947850fe9e51da9507732eb225c4570c70ba85abc3f6447745caa35a07abc776dda4ad4628dd3f5b18ff22c2db706892b1be21c22af7610741e5cf86e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef30b00000000002e09430000000065d3956b000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09440000000065d39589000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09450000000065d395b0000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09460000000065d395cf000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09470000000065d395f0000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09480000000065d395ff000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09490000000065d3960b000000000000000000000000000000000000000000000000000000000000000000000000009896800004000000000000002e094a0000000065d3960f000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e094b0000000065d3961b000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e094c0000000065d3961e000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e094d0000000065d39633000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000090f88e83095ee7850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000021759f64c83104ec2a09330353ad9be3182c80ca58f3649af512bcd3424fff130ed77c008347237fdeda01d74b2cc8fa6adc3e8ce9b8c25e0aafac72047e3c1063ec38567af89a532626d00000090f88e83095ee8850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000001f69716f083104ec1a0bc0c0907a0f62a6abb89c97c7a55d264a0b10cf60e6d1196fe059a7d9f2b19e1a00f3d9f1259dff72665225bb800b1466eeed0fd02735b3c92c95fdfb896f83191000002d3f902d0823a258501e36428ca8307a120946654b9c2b98d2bc001fe8937d6636b1398c61ccb80b90264c9807539000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002000101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008009703b19ba1ef5d2183a2c61662d840003b25606000301020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000004bd111ddda0000000000000000000000000000000000000000000000000000004bd129d6776000000000000000000000000000000000000000000000000000004bd2d56e700000000000000000000000000000000000000000000000000000004bd620f9de50000000000000000000000000000000000000000000000000000000000000002d1366501cec2a1f68a961d7ca643c9265250b89bcae9b5466056e484bb1363dc736bd49900d70c6949a3df834ba98eccbdba6dd4518e568495170a8bd61d235d00000000000000000000000000000000000000000000000000000000000000024355797f8a84aa2389015eb0164a4caac43c4e65b54e00741e4baa27d454f5756173ffce94b36c59359829fb7bc85d5399051980978126fc2f141e132fd0f1f083104ec1a0632090c89a02fde2ab53f90f0af194195c405898f39a68063a0c5ab00f4d78f5a024f9182869147f4fe9ed7f929d73e6111589fa8b7b8b31028d34e25f4e87c16800000090f88e83095ee9850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000001d0c2a26e83104ec1a06cc51f63e4426a0b87ec4208fe3924e586f49a0765d1e55f4cd7f99441a4e2dba0781e8d33ed09e31380ccc32b616b4ebb9d08c9b63b0100a066f2d82554e2812f0000006ff86d81ef8501e36428ca8308bf2f94b6099980ac5b82ca41a9e83dce308fa847fd72b980841249c58b83104ec1a0bed9797aa1f5bb66af7e29b39612472b0b111ac90fc5bd3aca88474a7e5a649fa06df16f171300c094990ac259fb322da9692b6af8a53b3f0ba31ba378ff2f6b3e00000090f88e83095eea850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000020825eb2f83104ec2a09a803bfefe496053746a8060ec6715ab5da7dd1ad97926b606bc2ee3ab2c9c9fa05b2a4d619ee92f66094119ee0537ea468850a0c40c96c984a5192aca56c39f4800000090f88e83095eeb850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000246808ee583104ec2a0d79078390ac38c7c2c89853172903273ff5e52dcd1dba4559770f968fe6d65d8a03f7a19da7022612765d68716dba73f100d6d3875c0312c77a51efc40be6ebc4200000132f9012f82acf68502b815597483035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027e60000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000024411ca8c000000000000000000000000000000000000000000000000000000000000001083104ec2a0de670e3cdc6c0b4315a21b6a819c568cfaf7f594b2caf34e105b3ec76eaf0880a0302068ee0d01bb8395b9dec72c01dc41c14222ce02c90ef801cc92989146312100000132f9012f82acf78502b815597483035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000028050000000000000000000000000000000000000000000000056bc75e2d631000010000000000000000000000000000000000000000000000000000000000aa848a000000000000000000000000000000000000000000000000000000000000001083104ec2a0bb6730ad150ba283937104180f764382921d938335f1af35ac5815687be875f8a06003c53e2028fc4e98e764fb5d13c196ca0b9304d51a9b64c14a316651d95a89000000aff8ad81f08501e36428ca82dc3a9404911f8c48c2fef593709fb7ea3cd5fbbf3f4d5380b844095ea7b3000000000000000000000000837a9f1803d84a341a343f05be9705dec4b81f930000000000000000000000000000000000000000000000000000000000000c0283104ec2a026e1074abeeb5dbf6099b7083064e9dd62947bfd86697fddb12ea259e7cb60a1a057184aa20dcbe37a7b896d8d5dc843baf686b4617ac6ac1153a92b6cbe41a41900000132f9012f82acf88502b815597483035f30948b14d287b4150ff22ac73df8be720e933f659abc80b8c43161b7f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000027e90000000000000000000000000000000000000000000000056bc75e2d63100001000000000000000000000000000000000000000000000000000000003862305e000000000000000000000000000000000000000000000000000000000000001083104ec1a01bc4065aa9f5e6bae3fb384e0aa9ac77284751deae385b2c5c99447df2f53ed3a05f336bd67430e9b7beedd51a3fab1a274c042e3f1b1aa8809c77424608f4519e0000008ff88d81f185024411ca8c8309bf1394837a9f1803d84a341a343f05be9705dec4b81f9380a4c62ba3700000000000000000000000000000000000000000000000000000000000000c0283104ec2a0c540f53525cbd8dffb46b4982a9f627e54b185091c600bf744f25eef48958ff3a00362a728f39a428e7ad00f40b5bbc651aceacce6531830941c7097428651f28d00000090f88e83095eec850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002263ed4b183104ec2a07d3c27d488a9db675d62ac30101b78d764d4cf355b2712eee342f9cfcc4302b2a041cd677777254d5ad74fec4268c8ed472062d978949ed1f7015d79e3d809c40d000000b0f8ae827dec8501e36428ca82f9e5946f1da9076f36000d5f9decdebd52402b1410b2c580b844468021b70000000000000000000000003e54b00786b506f840c8ac6e7fe38d27c0279953000000000000000000000000000000000000000000000000000000000000000083104ec2a0e0acff9653332fda27204b109def48cf7a49bbded5388ff7d51bdb91e44354b7a032f24d767b22b0c4451a92279ce521dfb7c9784ebfe14affd3045cbc201b40770000000000000000000000000000000000000000000000000000000000000000000000000000000000000037830d00000000002e094e0000000065d39636000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e094f0000000065d3965b000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09500000000065d39664000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09510000000065d3966d000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09520000000065d39679000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09530000000065d39688000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09540000000065d39697000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09550000000065d3969a000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09560000000065d396b5000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09570000000065d396d6000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09580000000065d396dd000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e09590000000065d396e3000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000000002e095a0000000065d396e6000000000000000000000000000000000000000000000000000000000000000000000000009896800001000000000076f87481f285024411ca8c82ded1945300000000000000000000000000000000000004880de0b6b3a764000084d0e30db083104ec2a04ae9f46ba98324ed1e2bfa7778f47d6d750bfd3d29a63afe89cf46481ed9437da01d7cbe64ef8c468cb9f77ab07be1f08d2454fe3ddde950b28599b506dfc8791f00000090f88e83095eed850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000001f3c4641783104ec1a0cbf3cc86d54fbb028e4f003dc794c2be16eac6e84b0530d9968a907f65b5aebaa05e5b1fbc1de475e49fa379aa8b2b9578366c0687520b1d6f017a9cd2169b6d5f00000090f88e83095eee850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002322cbd4983104ec1a06f02597b0a0af49d5795006597551ea5b1bc02213b7a40256ad4b2652849afdba03ee14cc97759ca3d18bfa79d1827f2ef6e8e2242d401f9aec92d0e72d9f2b5d100000090f88e83095eef850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000002721264d983104ec2a0da563a4d835f374a6eecd1b25a5e58895f3c652688d48260f657f4e22e77b715a02111736b4e5206550ae7ea147dc2e96832e8b4e8dcfe5f24e8ce7d29c9813ebf00000090f88e83095ef0850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000223d0183e83104ec2a0b933a92c6fb3057e04d2391c7685283f981ebb14e659270148ecc5a15d0fd308a070a19824425e99cde2c236e20fc4e51a2d288a467d52b8993b602a51d815b36a00001453f9145082028c85024411ca8c83039c0294e432150cce91c13a887f7d836923d5597add8e3180b913e409c5eabe000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000013a0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000008274f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001de1c919b368a0c7adcdf8d2f878e8a03e5ec543fb752beb452b06bbab102f37c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013617070726f7665436f6e747261637443616c6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000d294975a1e0994ad2d835c9431ad9cb131fffde657bdb33cf037e266f1a85ad65397b4db3ea24bac6e092d4bdb21f4fa84638eeddd3f1d0ca50afc80929e90007be95aee904d34013c00531f5d6c23084e5cc4e4000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000094176616c616e6368650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078386137373135663135413439446634623439346330444630333138393965434142326633314235350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000067600000000000000000000000000000000000000000000000000000000000000d800000000000000000000000000000000000000000000000000000000000000033000000000000000000000000033249f556213e290f64cebcca0f4ae59c2d62cc000000000000000000000000081bcea66fe8ca5e09a2d4425103a7804c6c1e270000000000000000000000000b6f38dae6acb217a07bf15f8fe8321537c83f43000000000000000000000000110e75163d776f0005e1448e7dba592ba21ca78300000000000000000000000011fdde73fb55ed8a8bba92f3e9fcb762fa6ba4fe000000000000000000000000190d382a335eb4b0f131324299ff8225c9ae911d0000000000000000000000001e31a5efedc34abd89449e88bb003f26e6decf03000000000000000000000000206b1b84f594df59b9cf23cea94be1178c2048f4000000000000000000000000219cc470e99b70d9111dcfa025ce9a1ea38ed507000000000000000000000000239eb449a60cd10eb265c413e62282820362eea000000000000000000000000023c6efebc5b4d1cc38fc74a4ea96de1703d387d400000000000000000000000027c28c665e25cd8023f442dbcc8abab87bb2538300000000000000000000000027cda603b1ae1b1cb1b4cfba6351866f374262910000000000000000000000002d00ca80a286528cc8611db5b9263f0fb6e3504600000000000000000000000038be7812944137d3dc2b78281ea0eb55a421293b00000000000000000000000048b4c3be622660924cdc64667e14cdd8ce348ddb000000000000000000000000493254f114efe1eed187e5f8d7dd7a367f5024f9000000000000000000000000537518a5ba6c3b506d9c2214cb518beaae867bf70000000000000000000000005d18e78132aedf86f2a405d8d2933dc204a73be90000000000000000000000005fe789148085683fac0dcce989bfb807271e8d580000000000000000000000006102c805a85e14853e1e3cfb5483304c6d08c59d00000000000000000000000065bfa0ddf26e90545a4fcdbdc4d7b7eee735b82200000000000000000000000068a4fb59f7f3022ccc18752f98f70e04d6d16ae60000000000000000000000006e23f8928fde6ce4b60d95f224be452538beafdf0000000000000000000000006fb12e213b0ea19cbb31ab307077acd591061ca200000000000000000000000078e79d001165136c07b85d4ec945a28b8552b1a20000000000000000000000007a3bf5cab8492d57ec310524430f77b96d0502580000000000000000000000007b68b88f9f8751ddd3692e83a230e0228133f9e2000000000000000000000000819c4c684ac7a1f3bfbaa556535851e2e82e2e650000000000000000000000008298182a159be69b96e5ebf94faef1bacb6501410000000000000000000000008c30107fdf21c3ec344b018190a7c22377536aff0000000000000000000000008f5de99cdb8795fd8c60dded53541ea9bf4c8a790000000000000000000000009218f827015c3191d2c61faf38c2fd64c1491e5d0000000000000000000000009790b9b287c51278b00fdbab2715b01e01d2c8210000000000000000000000009b2f17f65195a904a783bfdc1c40c306d643aa18000000000000000000000000a2d9f6b5893f7427af8978728c38d16b681a2824000000000000000000000000a41090021ba7833c2e34dd25650a191d720e4fbe000000000000000000000000aac7332380cce90e64a0b66f7d5c045b81573141000000000000000000000000abe6af325077d6041047beff59dc4ae71488c600000000000000000000000000b55bb609245d7113d610f29720e90740c6649f52000000000000000000000000bf971c96714c5198ad8a8ca001ef1c5eed790c6c000000000000000000000000c30836594698699f8f0107e35f98da6040ddf436000000000000000000000000cde01607a0d73d9ec92c6a0894684797e34b5829000000000000000000000000da664a27915cd9cbbd44100ed4276b6c4eadcec3000000000000000000000000df7468a4e4e87774d8678e864159d0f782024d2f000000000000000000000000eccedd1eef19a3efd143973a8cbfec3c590b42ea000000000000000000000000f06f2a8de1bf5a053b0b61434b57744a07896ee8000000000000000000000000f100598e3c48018c54d7d7107109f2be5330323e000000000000000000000000f50829c33d1b9c3af30816167637de6a290249ee000000000000000000000000f9efca275a19f8a397051eb4e6cf82626d9b68f9000000000000000000000000ff830d66181e909399ac67122db81588265df026000000000000000000000000000000000000000000000000000000000000003300000000000000000000000000000000000000000000000000000000000003b5000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000008d00000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000016900000000000000000000000000000000000000000000000000000000000000db00000000000000000000000000000000000000000000000000000000000001d50000000000000000000000000000000000000000000000000000000000002275000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000222200000000000000000000000000000000000000000000000000000000000021af000000000000000000000000000000000000000000000000000000000000023500000000000000000000000000000000000000000000000000000000000000ab00000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000000cd000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000000980000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000031300000000000000000000000000000000000000000000000000000000000000f400000000000000000000000000000000000000000000000000000000000000e70000000000000000000000000000000000000000000000000000000000000066000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000b5000000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000013e000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002d7000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000001cc0000000000000000000000000000000000000000000000000000000000000091000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000008d00000000000000000000000000000000000000000000000000000000000001c40000000000000000000000000000000000000000000000000000000000000098000000000000000000000000000000000000000000000000000000000000009500000000000000000000000000000000000000000000000000000000000000ad0000000000000000000000000000000000000000000000000000000000000094000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000041bbaa3a77159aa2fc0379b6d768ab86d2ba9a8579e368f87c3c6e851bac81f33f58d1eacd586df9baee76ecf9bb237f97d0bfb32e8e088c986c3b3424475e34511c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041e5969a78bd78963c8dd14ac0cfafa823265a6ca0a8b398c27fe5d7b8e886405503e970f90e5546ac9ce71226940cdc7bd18355e74a7ce31c02a8def16f60e5931c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041391480aaeae91065233d3fd00df52d11053e88d82722153fe1ad1689ff01eab00c524ceb8724edeb5f40f52f7ead3acb2dce94803481205c3ee1f5070e9f92541b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004104bf27683a23130991eedc9081f391868a9bec736ccb9875ab8adc9ce0aa3db342766f7c5e4b9961add18ad7fccd5f293a0c3ef0a171c3eb3c18cccbe9bcd21e1c0000000000000000000000000000000000000000000000000000000000000083104ec1a0b15a684e23a383fa38c20745258ad60e09d33ae0abd5fc68e4b0bbf05d96ee60a05b278f056e32a7c0a816b6e203213a96c258c5e41f5cf942b9e1ba1cc81e1ad600000090f88e83095ef1850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b50000000000000000000000000000000000000000000000000000000241d8929883104ec1a0317bf101b0b1df3b41afdec9409ccc9a35de4e677c3e87156595683960956219a02337fb98d0b9fff165da8861b3cf82e4ba4efc9c9bee0cee2c8bee92021ec2ee00000353f903508202898501e36428ca8301666d94d294975a1e0994ad2d835c9431ad9cb131fffde680b902e449160658de1c919b368a0c7adcdf8d2f878e8a03e5ec543fb752beb452b06bbab102f37c000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000094176616c616e6368650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078386137373135663135413439446634623439346330444630333138393965434142326633314235350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000311187a5620000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000014f1b903291b1c06dcdd3d27a3bc5e5f090d3fb96600000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001487243a1ea236745296ab9a0a736d594de956b32c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec2a0f028ddc11bb82089307e033b0ac188f2c0de667a590b95e7a68c01b0b717cd3ea00e589d70278e45962b5f77035550afadcdd42e74517d1085e4444cfbb478d34200000090f88e83095ef2850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000021d839cb983104ec2a00c527a67e116cec91dc991b358a897a5f62ed23068b71a277a95ad9425ab24e7a040b564a3ea2eed0921fe9aa161d87169c3b077b08cda5f8d4d581003cc1ba5dd00001453f9145082028d85024411ca8c83039c1a94e432150cce91c13a887f7d836923d5597add8e3180b913e409c5eabe000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000013a0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000008274f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000137dcb33c5c5607caf978b1ae9d491f3b1ca487a5c1f5549a39684dc08b1f2ad9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013617070726f7665436f6e747261637443616c6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000d294975a1e0994ad2d835c9431ad9cb131fffde66de08d195df5e958c427d900c2c831cde589d75c90960fe56f9f2a5c59f5300d8046cd732d2059232040f2b6c148cea1793769d5e85b27a1536b49a0acf724df000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000094176616c616e6368650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078386137373135663135413439446634623439346330444630333138393965434142326633314235350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000067600000000000000000000000000000000000000000000000000000000000000d800000000000000000000000000000000000000000000000000000000000000033000000000000000000000000033249f556213e290f64cebcca0f4ae59c2d62cc000000000000000000000000081bcea66fe8ca5e09a2d4425103a7804c6c1e270000000000000000000000000b6f38dae6acb217a07bf15f8fe8321537c83f43000000000000000000000000110e75163d776f0005e1448e7dba592ba21ca78300000000000000000000000011fdde73fb55ed8a8bba92f3e9fcb762fa6ba4fe000000000000000000000000190d382a335eb4b0f131324299ff8225c9ae911d0000000000000000000000001e31a5efedc34abd89449e88bb003f26e6decf03000000000000000000000000206b1b84f594df59b9cf23cea94be1178c2048f4000000000000000000000000219cc470e99b70d9111dcfa025ce9a1ea38ed507000000000000000000000000239eb449a60cd10eb265c413e62282820362eea000000000000000000000000023c6efebc5b4d1cc38fc74a4ea96de1703d387d400000000000000000000000027c28c665e25cd8023f442dbcc8abab87bb2538300000000000000000000000027cda603b1ae1b1cb1b4cfba6351866f374262910000000000000000000000002d00ca80a286528cc8611db5b9263f0fb6e3504600000000000000000000000038be7812944137d3dc2b78281ea0eb55a421293b00000000000000000000000048b4c3be622660924cdc64667e14cdd8ce348ddb000000000000000000000000493254f114efe1eed187e5f8d7dd7a367f5024f9000000000000000000000000537518a5ba6c3b506d9c2214cb518beaae867bf70000000000000000000000005d18e78132aedf86f2a405d8d2933dc204a73be90000000000000000000000005fe789148085683fac0dcce989bfb807271e8d580000000000000000000000006102c805a85e14853e1e3cfb5483304c6d08c59d00000000000000000000000065bfa0ddf26e90545a4fcdbdc4d7b7eee735b82200000000000000000000000068a4fb59f7f3022ccc18752f98f70e04d6d16ae60000000000000000000000006e23f8928fde6ce4b60d95f224be452538beafdf0000000000000000000000006fb12e213b0ea19cbb31ab307077acd591061ca200000000000000000000000078e79d001165136c07b85d4ec945a28b8552b1a20000000000000000000000007a3bf5cab8492d57ec310524430f77b96d0502580000000000000000000000007b68b88f9f8751ddd3692e83a230e0228133f9e2000000000000000000000000819c4c684ac7a1f3bfbaa556535851e2e82e2e650000000000000000000000008298182a159be69b96e5ebf94faef1bacb6501410000000000000000000000008c30107fdf21c3ec344b018190a7c22377536aff0000000000000000000000008f5de99cdb8795fd8c60dded53541ea9bf4c8a790000000000000000000000009218f827015c3191d2c61faf38c2fd64c1491e5d0000000000000000000000009790b9b287c51278b00fdbab2715b01e01d2c8210000000000000000000000009b2f17f65195a904a783bfdc1c40c306d643aa18000000000000000000000000a2d9f6b5893f7427af8978728c38d16b681a2824000000000000000000000000a41090021ba7833c2e34dd25650a191d720e4fbe000000000000000000000000aac7332380cce90e64a0b66f7d5c045b81573141000000000000000000000000abe6af325077d6041047beff59dc4ae71488c600000000000000000000000000b55bb609245d7113d610f29720e90740c6649f52000000000000000000000000bf971c96714c5198ad8a8ca001ef1c5eed790c6c000000000000000000000000c30836594698699f8f0107e35f98da6040ddf436000000000000000000000000cde01607a0d73d9ec92c6a0894684797e34b5829000000000000000000000000da664a27915cd9cbbd44100ed4276b6c4eadcec3000000000000000000000000df7468a4e4e87774d8678e864159d0f782024d2f000000000000000000000000eccedd1eef19a3efd143973a8cbfec3c590b42ea000000000000000000000000f06f2a8de1bf5a053b0b61434b57744a07896ee8000000000000000000000000f100598e3c48018c54d7d7107109f2be5330323e000000000000000000000000f50829c33d1b9c3af30816167637de6a290249ee000000000000000000000000f9efca275a19f8a397051eb4e6cf82626d9b68f9000000000000000000000000ff830d66181e909399ac67122db81588265df026000000000000000000000000000000000000000000000000000000000000003300000000000000000000000000000000000000000000000000000000000003b5000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000008d00000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000016900000000000000000000000000000000000000000000000000000000000000db00000000000000000000000000000000000000000000000000000000000001d50000000000000000000000000000000000000000000000000000000000002275000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000222200000000000000000000000000000000000000000000000000000000000021af000000000000000000000000000000000000000000000000000000000000023500000000000000000000000000000000000000000000000000000000000000ab00000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000000cd000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000002c7000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000000980000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000031300000000000000000000000000000000000000000000000000000000000000f400000000000000000000000000000000000000000000000000000000000000e70000000000000000000000000000000000000000000000000000000000000066000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000b5000000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000013e000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000002d7000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000001cc0000000000000000000000000000000000000000000000000000000000000091000000000000000000000000000000000000000000000000000000000000008d000000000000000000000000000000000000000000000000000000000000008d00000000000000000000000000000000000000000000000000000000000001c40000000000000000000000000000000000000000000000000000000000000098000000000000000000000000000000000000000000000000000000000000009500000000000000000000000000000000000000000000000000000000000000ad0000000000000000000000000000000000000000000000000000000000000094000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000041b37d42e16595ed3c7c21ae4dc29a666b76cf795ab766a32d73ced5440cba00e21fc59c35a079f2e30d4203b544c88de467e86fbb832e775599ecdfd0055a1a971c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041492175dafedaee3a8d19dff1841e3c3589bef2fe92fbabb913e547e92b688f645da650b1c111b3bd2ce9a79f8ae8497fb264229ae503e978a57e345551b742e21c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041334bab0c1c292d5f826531dd4c8e91ad7e83086c6352473263ad2bf226aeefd14e21d18eaf1913c2fd1bd9db434caea30ecf392dd4aae97be93dd8fd593c5dc81c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410c9c24a0a5e51dbd68e42c43717d707009166a9b5551bcfff6a3c5786dc1aa28753ae4a3d1c088d0a2bccb9609db15ef0d367f3703a86470581a37a00dd848231c0000000000000000000000000000000000000000000000000000000000000083104ec1a0ee98cd9114d2e2513c10ad8ecb0024eb139eec9d3fbfa7ca1d6133f9e037fc2da05eb3348523469f454b9221de1a296df868d5a7b70a6341a4511e0b3656071ec700000090f88e83095ef3850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b500000000000000000000000000000000000000000000000000000001edb1d70f83104ec2a05c0e35069946f01d77efa056a197b821440f3dee7325ac163a50a32ba5f0e48ea016184df3ac04913328d36d54de2693ed38f6ecb0c60ca06210703666ea5d5f6600000353f9035082028a85024411ca8c8301668694d294975a1e0994ad2d835c9431ad9cb131fffde680b902e44916065837dcb33c5c5607caf978b1ae9d491f3b1ca487a5c1f5549a39684dc08b1f2ad9000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000094176616c616e6368650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078386137373135663135413439446634623439346330444630333138393965434142326633314235350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000001d81d22b7507c0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000014ebb18bb1aba7b1f7d496144c96e9171c89caf5ad00000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001487243a1ea236745296ab9a0a736d594de956b32c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083104ec1a049e0196776a9d3347d1b5757d2ec3eb459327b132c0693768f8e1a6f80ce6220a06e973c19fa857e38e1692bc495f4e265d696634bcad0a3ae93db4ee74cf6596e00000090f88e83095ef4850192d3775382a4f294530000000000000000000000000000000000000280a4bede39b5000000000000000000000000000000000000000000000000000000021848e73883104ec1a0e17ebe9b5b839af04b64f7211719cb609c24659590ec941f553ece05c0accb6fa078afb8ae70fab38da0222e283a3f658ab2817889383d5edee08cd7ec6c1dad79000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", + "last_batch_index_proof": { + "proofs": [ + { + "key": "0x9c", + "proof": [ + "0xf90211a000b115159e2cf274be4a480850f31ade5f2d5c8250e9df5cd5bc7ea60781dfc9a00c20f366dd9f8a1bd3aa464331c27776df116f49249e97e8a07bdc63b576c20da0731bdb9e0e63d8822eefee6a128fa6d08a1ef785634afa846be81702ef39ba0da0a06590528dd0e27f43682ed353ab3efb997b1e38cb72a5449fcbf9f14927a92aa08becca8e365d7e2ceee9ae7b1f0c4a66303ea3ec8a1bed63cfa48daf0f885ed3a0e05b013911fd1834327cb92e4f7e74bb38ff6f92d0ad0d1d87695b69ebf62c70a0ae6412f3d352a6cdfc06fefbbcd9845bb9b3422c205ddbc8ecd90f18207abdf4a001ffce658c9b2a5b32797f611fafaa21c107dd88cc43aebbd1b8be318f34d10ba0145cddbde2805661d5fc9d8bfac4a3168e9ce299d84c29df85833a5b8a8b0d4fa0a47ce92df9913f3ed8e7a178fb6f8ced040b8c1dcb1076b8280301a50a329d52a07de1c1f78a7dfb5aa2b84e09a837e381b1517d14aa15468b8297977ccb3f04a1a0cc4ed37c770000e3748487aa32ccba9112dc5997884868e5228f200aa362bb56a00ecaeb6cdd27af319aeaf3445b3f771582ceb20d41c480d60f0560712f674dffa016a1a71f7227fb0bdc22f531539053c4136335512e833b9a8a7a0f713f26957ba097c8879c188a6b63a542e61705dc3048a6403b33ed4d2b8f1f873010ce58a61ca007fc91a96001e1f19cf482aa39fc646ceead15370bb6f8d0a9277c47adbf36d680", + "0xf90211a05b750b6cf206416969c471d941426156c0472519ecb5ee0452e39fe60ec13041a0c7768fabd00123a900185a194ca9a3419f85631dddc45f9287f28b4db55606c6a0bab22b0556e633eb800aa2d20e24267948bb682351e2708a5523d5896fc59bc3a00ecc8f4d24fc6f75fe919e7a369b079ea938df69c72cec32bfa92c76494677d9a041839161aca73b444bfab2133c6a2062650849d96ab2828f905c34c09d635571a05df355b13108916829e1605b8f20cac4577ddf621545cd6ada1d584a5488202ca08b9220734d521ffd050d7b261ca7e3c8ac6078a24c13d285db440c8995db44dca0e227610b1ddacf727b305bd159d56287b051ca74ac9cdbbd91af26054683c1a3a0d32c79db33ff29cb146084b484d0b4009890f969e63c17df50f0c2e0ea5b8bf6a06c5c740dc907f226374a0d7185c908672f75f4a60f8f60aaa9f4d95adb72304ca0b2faf70749a7c5dc6bfd3aa20b5063f9dcdaf5d9439c819e926f8fccb4643d15a043655f1e55b184a8843bd6bd35dc5ea8beb556f683a7af9437cc5e57c6b30b34a00a0a6b589fc3e78f0499f019287a18f550559b94382f4450b06860a006a76991a0b24687e06450231c4cbfe158d859420ee63096a42170e4faf57c76aae5a15d4fa0316541dbf5d9a09ca412bf5646d4549ab4fd6bce6bc17cb3c8dc670c85e5268ca05ac1e000d0de0cc6877e3e6b369a447e92c973bc8240dd9f029e1d2d8f826eda80", + "0xf90211a0d11c7773cbed184d7af1c7d90651bde6fa71aa75515bbfac5c04ffae3e084503a07e8674f6a909ef0a0df6de4bc9d6b0a7fa5e7ee520db7469dbd8b82b49bdad23a082f94861336b402c423391104cdbd80ea29130e36a914c1b8c74ae0164fb0ed8a0f513f5a83ea14c5dabb2df6d547b12aa213a25d43dcbd658961397e01236d8fda0e8a09da79fd176bcefec37b66eab9ea5f2c9d262b7857fa3a7e8134b0d81fef0a078c20880032f2a123c9e1b5b00a3d49ef4d8be3a003cd04c8a5ed06a334b61c3a016b31a5651e8a94b8c37c85627f02254a1786642d9e0182995493ecb3a67441fa0b1b2f8c1d7964c93f7674d5cb0b22b473ecd9c9783af81e5130a6c90cbc45b15a0515753da7a057565fdca518f5450f3d3fcc9668d656321cec29477eedc93c488a0a4c08feea1859b7f3be9e61356b9e1c749537ee7922a0639dbe91bd8abe179f3a0e1e5a44573dafef2ea80ad5371ebb46c84840c921bdbd38ff326eb08b66f34cba085a86bbc26cdc4bf3dc571e28545c70fbb725a683caa794a03e533e988ad4059a0e31862a7c047e2ae7f4764f10f1442ca1ce385b13a564cce09b5e34cf9c451dba0c41a7c299a652cd8cf7929af96a06c033a60f779469eb5d84908194c63c59a48a06dcd21d9616300450f2a3298524a652bd1b7dd6732950bd9189cc4acb42b8058a01e94024cb216dc86785d5e4e3d11e25392d6680a67bfe294fa347ce932bb124680", + "0xf90211a09a3c2ec24073de2a6356fe12ff5ff5402a5632ac5d877c4998ddd03ad9f9d993a082fe50db7111be23176def6ada1e0952be361d04f73f09d9d2481dfb1010c3eba0882abfbf12225fc442e1a89de4c560a0f959256255c7f3c1861f0eefc7ef2a94a03c4e6f65f17ab56fdcf908ed3adc800ceb0b876e10b91a511d58ca250d4f826ca0800a06dc4a48928d47fa8dcccc580a54ae1a01f73aeb5ae434ed835cd6b8ae93a03d6e6ba19adbf06602b4261ff744c7de8e4b3f70920d5e66146c5c7b3efad4e0a014f181d4227392c10886d8669cbeaf40a50172e3ef089bde7b31c65a7188566ea090935c7bf50aaeacd868db04440c21210f4d1f3dddc82dfe4e22c91985c25ca8a0a77894b6ccfa6e524e3a42e820da14edee8dd8c1531038f93448f778fac337a9a0456e980c5f0926f191efa5eb82e8d24243ea8cb07996375677f01029a0c903daa0b033c89299efa1d44e37f7a8ae8882c594ef5c789cff1b0ada90cab2f65e9d3ea034602d86244b1da55b25fa63b912dd78fe2cee4f2539fb46e5130708420a30cea0c55f1065cf835b31e11ec7ff8eaec1a5b31cae3e620a05b107c15282df1ab3cda0709718af75ea26c1eb3eff249c5b0a99d87acf7a8a13a889f6efc10cfae9db75a05be22cb81afc07e3038091c5de6d7935521901a649743b0f840b91187ab228a0a0af9953223cdd4bfd048310e585587a099761b6ed4ce64142630125c9bab198d880", + "0xf871a0aa1ea7ff8c84dc737bed63bdccbc0437b588b4b6e2b208a65c7ce1256ae9b8ae80808080808080808080a06609c08afdd2820f09138014ee98ebdc455eea1e96ffe53fa464f5aab3890dab808080a09ee310043956460be2c3e3b1da189b120e7a1c9c24d8892238c71f6ec4e1a50b80", + "0xe39e39071dfafeac1409d3f1d19bafc9bc7c37974cde8df0ee6168f0086e539c8382ff2f" + ], + "value": "0xff2f" + } + ] + } +} diff --git a/lib/linea-verifier/tests/scroll_proof.json b/lib/linea-verifier/tests/scroll_proof.json new file mode 100644 index 0000000000..62da2699dd --- /dev/null +++ b/lib/linea-verifier/tests/scroll_proof.json @@ -0,0 +1,11 @@ +{ + "key": "0x2", + "value": "0x5ca4ec2a79a7f67000000", + "proof": [ + "0x092ae559c4a5791aa624938167828ea4509d88eaa82114504464c72cbd682e1fd1061c6d68c9639dab7cf8bfb78aadeca93a9bab93dbed21a2c26c92b8877a99e9", + "0x080b57786fb3f84de0a36e57cb2c13baae5ccffd43be3f75c5590d473128811fc40000000000000000000000000000000000000000000000000000000000000000", + "0x0618b0b7a56d619daa0810e8137a70bf2dc724490c94c53dc8fd63b5446a881f960d7c59168bf3ce47e73bf8eed28a9e2968d2d08442b3548b6ec3f94d530dfd17", + "0x04020953ad52de135367a1ba2629636216ed5174cce5629d11b5d97fe733f07dcc0101000000000000000000000000000000000000000000000005ca4ec2a79a7f67000000200000000000000000000000000000000000000000000000000000000000000002", + "0x5448495320495320534f4d45204d4147494320425954455320464f5220534d54206d3172525867503278704449" + ] +} diff --git a/lib/linea-zktrie/Cargo.toml b/lib/linea-zktrie/Cargo.toml new file mode 100644 index 0000000000..fc573c97c1 --- /dev/null +++ b/lib/linea-zktrie/Cargo.toml @@ -0,0 +1,19 @@ +[package] +edition.workspace = true +license-file.workspace = true +name = "linea-zktrie" +repository.workspace = true +version = "0.1.0" + +[dependencies] +gnark-mimc = { workspace = true } +hex = { workspace = true } +hex-literal = { workspace = true } +serde = { workspace = true } +serde-utils = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } +unionlabs = { workspace = true } + +[lints] +workspace = true diff --git a/lib/linea-zktrie/src/lib.rs b/lib/linea-zktrie/src/lib.rs new file mode 100644 index 0000000000..f2bcdc7d85 --- /dev/null +++ b/lib/linea-zktrie/src/lib.rs @@ -0,0 +1,2 @@ +pub mod node; +pub mod verify; diff --git a/lib/linea-zktrie/src/node.rs b/lib/linea-zktrie/src/node.rs new file mode 100644 index 0000000000..9641d43636 --- /dev/null +++ b/lib/linea-zktrie/src/node.rs @@ -0,0 +1,172 @@ +use gnark_mimc::{mimc_sum_bl12377, MiMCBls12377Constants}; +use serde::{Deserialize, Serialize}; +use unionlabs::{ + errors::{ExpectedLength, InvalidLength}, + hash::H256, + ByteArrayExt, +}; + +// https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/path/PathResolver.java#L27 +pub const SUB_TRIE_ROOT_PATH: [u8; 1] = [1]; + +// https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/node/LeafType.java#L21-L24 +pub const LEAF_TYPE_VALUE: u8 = 0x16; + +// https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/path/PathGenerator.java#L34 +pub fn bytes_to_leaf_path(bytes: &[u8], terminator_path: u8) -> Vec { + let mut path = vec![0u8; bytes.len() * 2 + 1]; + let mut j = 0; + for i in j..bytes.len() { + let b = bytes[i]; + path[j] = b >> 4 & 15; + path[j + 1] = b & 15; + j += 2; + } + path[j] = terminator_path; + path +} + +// TODO: use bitvec +// https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/path/PathResolver.java#L82 +pub fn node_index_to_bytes(trie_depth: usize, node_index: u64) -> Vec { + hex::decode(&format!("{node_index:0>trie_depth$b}")).unwrap() +} + +// https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/path/PathResolver.java#L71 +pub fn get_leaf_path(trie_depth: usize, node_index: u64) -> Vec { + [ + SUB_TRIE_ROOT_PATH.as_ref(), + &bytes_to_leaf_path( + &node_index_to_bytes(trie_depth, node_index), + LEAF_TYPE_VALUE, + ), + ] + .concat() +} + +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +pub struct EmptyLeafNode {} + +impl EmptyLeafNode { + // https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/node/EmptyLeafNode.java#L80 + pub const HASH: H256 = H256([0u8; 32]); +} + +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +pub struct LeafNode { + pub previous: H256, + pub next: H256, + pub hashed_key: H256, + pub value: H256, +} + +impl LeafNode { + // https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/node/LeafNode.java#L56 + pub fn hash(&self, constants: &MiMCBls12377Constants) -> Result { + mimc_sum_bl12377( + constants, + [ + self.previous.as_ref(), + self.next.as_ref(), + self.hashed_key.as_ref(), + self.value.as_ref(), + ] + .concat(), + ) + } +} + +impl TryFrom<&[u8]> for LeafNode { + type Error = InvalidLength; + fn try_from(value: &[u8]) -> Result { + let values = <[u8; 128]>::try_from(value).map_err(|_| InvalidLength { + expected: ExpectedLength::Exact(128), + found: value.len(), + })?; + Ok(Self { + previous: values.array_slice::<0, 32>().into(), + next: values.array_slice::<32, 32>().into(), + hashed_key: values.array_slice::<64, 32>().into(), + value: values.array_slice::<96, 32>().into(), + }) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +pub struct BranchNode { + pub left: H256, + pub right: H256, +} + +impl BranchNode { + // https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/node/BranchNode.java#L82 + pub fn hash(&self, constants: &MiMCBls12377Constants) -> Result { + mimc_sum_bl12377( + constants, + [self.left.as_ref(), self.right.as_ref()].concat(), + ) + } +} + +impl TryFrom<&[u8]> for BranchNode { + type Error = InvalidLength; + fn try_from(value: &[u8]) -> Result { + let values = <[u8; 64]>::try_from(value).map_err(|_| InvalidLength { + expected: ExpectedLength::Exact(64), + found: value.len(), + })?; + Ok(Self { + left: values.array_slice::<0, 32>().into(), + right: values.array_slice::<32, 32>().into(), + }) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +pub struct RootNode { + pub next_free_node: H256, + pub child_hash: H256, +} + +impl RootNode { + // Same as branch + pub fn hash(&self, constants: &MiMCBls12377Constants) -> Result { + mimc_sum_bl12377( + constants, + [self.next_free_node.as_ref(), self.child_hash.as_ref()].concat(), + ) + } +} + +impl TryFrom<&[u8]> for RootNode { + type Error = InvalidLength; + fn try_from(value: &[u8]) -> Result { + let values = <[u8; 64]>::try_from(value).map_err(|_| InvalidLength { + expected: ExpectedLength::Exact(64), + found: value.len(), + })?; + Ok(Self { + next_free_node: values.array_slice::<0, 32>().into(), + child_hash: values.array_slice::<32, 32>().into(), + }) + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum Node { + EmptyLeaf(EmptyLeafNode), + Leaf(LeafNode), + Branch(BranchNode), + Root(RootNode), +} + +impl Node { + pub fn hash(&self, constants: &MiMCBls12377Constants) -> Result { + match self { + Node::Leaf(node) => node.hash(constants), + Node::Branch(node) => node.hash(constants), + Node::Root(node) => node.hash(constants), + Node::EmptyLeaf(_) => return Ok(EmptyLeafNode::HASH), + } + } +} diff --git a/lib/linea-zktrie/src/verify.rs b/lib/linea-zktrie/src/verify.rs new file mode 100644 index 0000000000..81f8112e2b --- /dev/null +++ b/lib/linea-zktrie/src/verify.rs @@ -0,0 +1,781 @@ +use gnark_mimc::{mimc_sum_bl12377, MiMCBls12377, MiMCBls12377Constants}; +use serde::{Deserialize, Serialize}; +use unionlabs::{ + errors::InvalidLength, + hash::{H160, H256}, + linea::{ + account::{MimcSafeBytes, ZkAccount}, + proof::{MerklePath, MerkleProof, NonInclusionProof}, + }, + uint::U256, +}; + +use crate::node::{get_leaf_path, BranchNode, LeafNode, Node, RootNode}; + +pub const DIRECTION_LEFT: u8 = 0; +pub const DIRECTION_RIGHT: u8 = 1; + +// https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/trie/src/main/java/net/consensys/shomei/trie/ZKTrie.java#L64C1-L65C47 +pub const ZK_TRIE_DEPTH: usize = 40; + +pub trait ZkKey { + fn hash(self, constants: &MiMCBls12377Constants) -> Result; +} + +pub trait ZkValue { + type Key: ZkKey; + + fn hash(self, constants: &MiMCBls12377Constants) -> Result; +} + +impl ZkKey for H160 { + fn hash(self, constants: &MiMCBls12377Constants) -> Result { + let mut padded_key = [0u8; 32]; + padded_key[12..32].copy_from_slice(self.as_ref()); + mimc_sum_bl12377(constants, &padded_key) + } +} + +impl ZkValue for ZkAccount { + type Key = H160; + + fn hash(self, constants: &MiMCBls12377Constants) -> Result { + mimc_sum_bl12377(constants, self.into_bytes()) + } +} + +impl ZkKey for H256 { + fn hash(self, constants: &MiMCBls12377Constants) -> Result { + mimc_sum_bl12377(constants, MimcSafeBytes::from(self.0).into_bytes()) + } +} + +impl ZkValue for H256 { + type Key = H256; + + fn hash(self, constants: &MiMCBls12377Constants) -> Result { + mimc_sum_bl12377(constants, MimcSafeBytes::from(self.0).into_bytes()) + } +} + +#[derive(Clone, Debug, PartialEq, thiserror::Error)] +pub enum Error { + #[error("invalid direction {0}")] + InvalidDirection(u8), + #[error("missing root node")] + MissingRoot, + #[error("missing leaf node")] + MissingLeaf, + #[error("invalid field element length: {0}")] + InvalidLength(InvalidLength), + #[error("invalid mimc hashing: {0:?}")] + MimcError(gnark_mimc::Error), + #[error("could not decode leaf value")] + CouldntDecodeValue, + #[error("invalid trie root, actual: {actual}, expected: {expected}")] + RootMismatch { actual: H256, expected: H256 }, + #[error("invalid subtrie root, actual: {actual}, expected: {expected}")] + SubtrieRootMismatch { actual: H256, expected: H256 }, + #[error("key mismatch, actual: {actual}, expected: {expected}")] + KeyMismatch { actual: H256, expected: H256 }, + #[error("value mismatch, actual: {actual}, expected: {expected}")] + ValueMismatch { actual: H256, expected: H256 }, + #[error("non adjacent node, left: {left:?}, right: {right:?}")] + NonAdjacentNode { left: LeafNode, right: LeafNode }, + #[error("key not in center, left: {left}, key: {key} right: {right}")] + KeyNotInCenter { left: H256, key: H256, right: H256 }, +} + +impl From for Error { + fn from(value: InvalidLength) -> Self { + Self::InvalidLength(value) + } +} + +impl From for Error { + fn from(value: gnark_mimc::Error) -> Self { + Self::MimcError(value) + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct VerifiablePath { + pub root: RootNode, + pub path: Vec, + pub leaf: LeafNode, +} + +impl TryFrom<&MerklePath> for VerifiablePath { + type Error = Error; + fn try_from(value: &MerklePath) -> Result { + let root = RootNode::try_from( + value + .proof_related_nodes + .first() + .ok_or(Error::MissingRoot)? + .as_ref(), + )?; + let leaf = LeafNode::try_from( + value + .proof_related_nodes + .last() + .ok_or(Error::MissingLeaf)? + .as_ref(), + )?; + // Minus root/leaf + let inner_path_len = value.proof_related_nodes.len() - 2; + let path = value + .proof_related_nodes + .iter() + .skip(1) + .take(inner_path_len) + .map(|node| { + if node.len() == MiMCBls12377::FIELD_ELEMENT_BYTES_LEN * 2 { + BranchNode::try_from(node.as_ref()).map(Node::Branch) + } else { + LeafNode::try_from(node.as_ref()).map(Node::Leaf) + } + }) + .collect::, _>>()?; + Ok(VerifiablePath { root, path, leaf }) + } +} + +pub fn verify TryFrom<&'a [u8]>>( + constants: &MiMCBls12377Constants, + proof: &MerkleProof, + root: H256, + key: V::Key, +) -> Result, Error> { + match proof { + MerkleProof::Inclusion(inclusion_proof) => verify_inclusion::( + constants, + inclusion_proof.leaf_index, + &inclusion_proof.proof, + root, + Some(key), + ) + .map(|(_, value)| Some(value)), + MerkleProof::NonInclusion(noninclusion_proof) => { + verify_noninclusion::(constants, noninclusion_proof, root, key).map(|_| None) + } + } +} + +pub fn verify_noninclusion TryFrom<&'a [u8]>>( + constants: &MiMCBls12377Constants, + noninclusion_proof: &NonInclusionProof, + root: H256, + key: V::Key, +) -> Result<(), Error> { + // left in root + let (left_path, _) = verify_inclusion::( + constants, + noninclusion_proof.left_leaf_index, + &noninclusion_proof.left_proof, + root, + None, + )?; + // right in root + let (right_path, _) = verify_inclusion::( + constants, + noninclusion_proof.right_leaf_index, + &noninclusion_proof.right_proof, + root, + None, + )?; + // N+.Prev == i- + if U256::from_be_bytes(right_path.leaf.previous.0) + != U256::from(noninclusion_proof.left_leaf_index) + { + return Err(Error::NonAdjacentNode { + left: left_path.leaf, + right: right_path.leaf, + }); + } + // N-.Next == i+ + if U256::from_be_bytes(left_path.leaf.next.0) != U256::from(noninclusion_proof.right_leaf_index) + { + return Err(Error::NonAdjacentNode { + left: left_path.leaf, + right: right_path.leaf, + }); + } + // HKey- < hash(k) < HKey+ + let recomputed_key = key.hash(constants)?; + if left_path.leaf.hashed_key >= recomputed_key || recomputed_key >= right_path.leaf.hashed_key { + return Err(Error::KeyNotInCenter { + left: left_path.leaf.hashed_key, + key: recomputed_key, + right: right_path.leaf.hashed_key, + }); + } + Ok(()) +} + +pub fn verify_inclusion TryFrom<&'a [u8]>>( + constants: &MiMCBls12377Constants, + leaf_index: u64, + merkle_path: &MerklePath, + root: H256, + key: Option, +) -> Result<(VerifiablePath, V), Error> { + let leaf_path = get_leaf_path(ZK_TRIE_DEPTH, leaf_index); + let verifiable_path = VerifiablePath::try_from(merkle_path)?; + // Verify the top root hash + let recomputed_root = verifiable_path.root.hash(constants)?; + if root != recomputed_root { + return Err(Error::RootMismatch { + actual: recomputed_root, + expected: root, + }); + } + match key { + Some(key) => { + // Verify that they leaf is related to our key + let recomputed_key = key.hash(constants)?; + if verifiable_path.leaf.hashed_key != recomputed_key { + return Err(Error::KeyMismatch { + actual: recomputed_root, + expected: verifiable_path.leaf.hashed_key, + }); + } + } + // For non inclusion proof, we don't know the key of the left/right + // nodes. We instead verify that the expected key is sandwitched after + // verifying inclusion. Hence, nothing is done there. + None => {} + } + // The value is decoded then hashed, the decoding is required as the value + // may need to be transformed before being hashed (ZkAccount keccak field + // that need to be split in two elements to fit in the scalar field for + // instane) + let value = V::try_from(merkle_path.value.as_ref()).map_err(|_| Error::CouldntDecodeValue)?; + // Verify that the value is related to the leaf + let recomputed_value = value.clone().hash(constants)?; + if verifiable_path.leaf.value != recomputed_value { + return Err(Error::ValueMismatch { + actual: recomputed_root, + expected: verifiable_path.leaf.value, + }); + } + // The algorithm we use is slightly more explicit, we actually extract both + // the root and leaf so the inner path must exclude both root leaf + // Minus root/leaf + let inner_path_len = leaf_path.len() - 2; + let inner_path = leaf_path.into_iter().skip(1).take(inner_path_len); + let leaf_hash = verifiable_path.leaf.hash(constants)?; + // Starts with the leaf hash, then recursively walk back to the tip of the + // tree. + let subtrie_root = verifiable_path + .path + .iter() + .zip(inner_path) + .rev() + .into_iter() + .try_fold( + leaf_hash, + |current_hash, (node, direction)| -> Result { + let node_hash = node.hash(constants)?; + let to_hash = match direction { + // We went on the left branch, we hash against the right sibling + DIRECTION_LEFT => [current_hash.as_ref(), node_hash.as_ref()].concat(), + // We went on the right branch, we hash against the left sibling + DIRECTION_RIGHT => [node_hash.as_ref(), current_hash.as_ref()].concat(), + d => return Err(Error::InvalidDirection(d)), + }; + Ok(mimc_sum_bl12377(constants, to_hash)?) + }, + )?; + // Verify the subtrie root + if verifiable_path.root.child_hash != subtrie_root { + return Err(Error::SubtrieRootMismatch { + actual: subtrie_root, + expected: verifiable_path.root.child_hash, + }); + } + Ok((verifiable_path, value)) +} + +#[cfg(test)] +mod tests { + use gnark_mimc::{new_mimc_bls12_377, new_mimc_constants_bls12_377}; + use hex_literal::hex; + use unionlabs::{ + hash::{H160, H256}, + linea::{account::ZkAccount, proof::GetProof}, + }; + + use super::verify; + + #[test] + fn test_account_and_storage_inclusion() { + let raw = r#" +{ + "accountProof": { + "key": "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", + "leafIndex": 65362, + "proof": { + "proofRelatedNodes": [ + "0x00000000000000000000000000000000000000000000000000000000000120fe0393507c456718a986386c7923fe68b87c29d83ac7f7ce1cdb49afc7e66a4771", + "0x008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809", + "0x060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b", + "0x0a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef322550a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef32255", + "0x01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d", + "0x090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484", + "0x11c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f30611c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f306", + "0x07f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d4807f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d48", + "0x0f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd80f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd8", + "0x0cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e27390cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e2739", + "0x014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b", + "0x11c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca55411c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca554", + "0x1092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c221092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c22", + "0x0969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a890969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a89", + "0x079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368", + "0x004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458", + "0x0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd", + "0x0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c", + "0x0defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea80defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea8", + "0x0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b", + "0x1276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f51276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f5", + "0x02a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b202a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b2", + "0x070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978", + "0x0133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed10133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed1", + "0x09cbd26c486bc2217bce59337120283f655a7ba65075f98059249f471812d0480b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f84", + "0x0c5c4d122720c4d6e7866d9b6bc6171c6259be90095976b665406ccf2dc6a8950305d7ebd7da4f82f061632eb7ec0c3060f51af848661d479bb64003f0fc5342", + "0x0c4762f6af9f09a529e70f0b34b7afafe2bba8944eccdcdb95cf13e0ff00ab2209d8b650f132967dba1764abe34c3d446311503ba7712d5f474a6e159b085b5f", + "0x0c3474a51e2654aca28b15add106ac676d92b9416ee788ac0b88873e77a009660176016fa85f1ba2375f784c72fae85763e12018e3e781c306f97ad9f826a22e", + "0x108459110262f154aef2d43fce77d314a7ec867f0068987716ff51582847e498009a6be6c408befa4eb7e6141fd427a2ce6489d50bf5f9de6bde9e100ada3482", + "0x118d3c53f9a3ea556029e867af93e9b4450cbacdf4dff29859e399ae16468e5102cefeff18d2980c8a9253c4609506472ba4764ea99efa6324dacf34740d9f05", + "0x005d88c799974510f99c04afdaba0f6b8f62edd55d8d89910009e148385a72c30a8fac91e2023660e8ac50ff082578361ba0901b16fe691f9b78044cbf6d1c4b", + "0x106f788c7d5990bec78f6c9cadd15604c99a8f1d56c875d324bb5ece63d83f3606694c69c43303aa1c614d60ed8fc66838f368b134cfc1ab00b6c83b2b5b3c8c", + "0x0ba8fdb8888982dde981f8e2cc9177c8c3ce0607661e113604e436951776de9c0b9ec8fec4b0696c73e04fd6bee4aa345633d23ef0c6bc4e4bbcf757af2677f0", + "0x0f5ae90881ea3398fd1a14fb83babc2335dfc4e6298aada1d827042d67dea48f0dee0a62e8ff86baddb091105d845c862089fe2f1963cd3798d636035da4d518", + "0x03d41bdb96726bf7f745784e42eef043c8b797f788d9720e36e460502e14c9fb0923e0e0228d2fe8619e30581e3e225d4e99e0daa011e15ac34c28fa30ea2989", + "0x11335bc4bf8a15d8c116cbdfe74242e80c7f60ac1a614d00f99fb9e1148126930502f7b7740708503e3858bc6df707cf4a1a751bcef3f2a5eb6eff9d8efa5cf8", + "0x0d60d90907794deaabe1e532a128e17ac94ec30339f3e367bc9ecd0aa40fd8b6009f71be21f99f29acd62b42787c99e5192646f808306fff0960ef5cd9a5ac16", + "0x0a6fd861ba25def420f5503fbbc4e0de2e54b4fbf0b22364e4a188eaf72ac58c02e49a2a28faca35409f471b4d981951aeabba2f091a427a2e88c53d1c7eeed3", + "0x0a93ecccd90368342584da9a8623e89a7a71d36f1da58d9874d50c045587138b0476d671e749bd2cd45fe416e1409caa22863f8cebdf926920a9f68b150d92d7", + "0x0821de61351452c22cf6bdafcd85be9a8cb3c2ad0af51f871d44221575785f9d12200803e31923cc68d6c9b906876643688e3a7ccb21264f933028b060564e4d", + "0x0000000000000000000000000000000000000000000000000000000000001c1e000000000000000000000000000000000000000000000000000000000000a354000036e661469dd70081ada16334d16a4049a124e261cd93def5fff88f85afda01b285fb7d6e0c7e05505a348777221f3c9fb491bbbbea4853e62e93f415efe7", + "0x000000000000000000000000000000000000000000000000000000000000894100000000000000000000000000000000000000000000000000000000000002db104a10331d6a854148a10b11c19cf2abae0412c9909ecefca54adc135ee57a950481fe75941093272afb1f8f76353afad6b89b1c19c383b07730c6f160b59243" + ], + "value": "0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000017d7498da306d2911280c3481d8b1510e16062ffaa631812c3ca53639329c1577354f0e4cd850dde1255c33de5e8c499e72ca1f49352847124c0dbfc30d0374d4d5d5e7cddb83c7ac93c806e738300b5357ecdc2e971d6438d34d8e4e17b99b758b1f9cac91c8e700000000000000000000000000000000000000000000000000000000000005c89" + } + }, + "storageProofs": [ + { + "key": "0x975227e2a924779fb36829b74e9ab66f8d906444c0efb23059aaf437a9254f64", + "leafIndex": 138, + "proof": { + "proofRelatedNodes": [ + "0x00000000000000000000000000000000000000000000000000000000000003a90c382f6158633dfaf5ea90b4b6aef05e0171d9c5e97a2f3aa41c3944e2d08f7c", + "0x008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809", + "0x060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b", + "0x0a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef322550a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef32255", + "0x01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d", + "0x090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484", + "0x11c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f30611c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f306", + "0x07f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d4807f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d48", + "0x0f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd80f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd8", + "0x0cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e27390cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e2739", + "0x014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b", + "0x11c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca55411c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca554", + "0x1092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c221092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c22", + "0x0969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a890969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a89", + "0x079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368", + "0x004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458", + "0x0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd", + "0x0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c", + "0x0defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea80defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea8", + "0x0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b", + "0x1276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f51276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f5", + "0x02a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b202a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b2", + "0x070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978", + "0x0133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed10133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed1", + "0x0b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f840b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f84", + "0x0f3f9cf1e5ba6bdbb6daafc405bcceac97270fe89265b6a0faa2ba4bfd5cbf5d0f3f9cf1e5ba6bdbb6daafc405bcceac97270fe89265b6a0faa2ba4bfd5cbf5d", + "0x08b60393196453ee74fdf240449d9aa2569875b43596ea2621eecda8d8909acd08b60393196453ee74fdf240449d9aa2569875b43596ea2621eecda8d8909acd", + "0x10c439d656480d21a08c068717556fb8104a7a76e26f60e393ce4e36ae21e07b10c439d656480d21a08c068717556fb8104a7a76e26f60e393ce4e36ae21e07b", + "0x09ea86c5cd59ac4bfca4e46e7b50bb37c8327350888ba71112ecf3f5093baaef09ea86c5cd59ac4bfca4e46e7b50bb37c8327350888ba71112ecf3f5093baaef", + "0x0b971345bfa43e192ca2fb1c9ddd19f2dddf461243b1a54fdd5a4d581f850c110b971345bfa43e192ca2fb1c9ddd19f2dddf461243b1a54fdd5a4d581f850c11", + "0x0edd0129edd35191a183ecd28cbcab2a48ad381215d8544acf35248639835dcd0edd0129edd35191a183ecd28cbcab2a48ad381215d8544acf35248639835dcd", + "0x0d052b80abb809f9120c6b9884fffd52dd230a8dea0e503ee37a657412f956e4124085568263d79db22e8138cdfcddb82217762c26573f47a99464a1891998c0", + "0x05e61cac7ebd2c56b6e841e2437573d262652dab2a93cf5c87ae6c77ea6e29620b2e2ac6538353a780d865eb117c6a15c9ce5482df3f82de22341ff53ff603bb", + "0x081d406e2e7c445affbd6879217ba8ef422de57833bfd2117c67132b7c136b80041dc4f76e0dcec4e22f176ab6a40e8cfa6f15fd3be71dffc508c7d1e49a095f", + "0x08f74df1f6c448f34dbebc04442406cccf4e59336dbdeb8820d056584f8e5c2e000d8662808f22994b99a5a7c5888e053462f631bd6ccc1bb5cfc409c6496e29", + "0x01535de3a78232579c22be9a44bacd4ab197dcab60c15cad6ee5783a87e8fe3e035ca4181a3a2b7660a12b44b972a9b13751b7765a87b943690afae72084dc70", + "0x0dc279f3ab0113621f49cce7fcd58b620db8940fa536685b0f085062ef5804500f809df436769c9dca43efa53adf5d802e5d9a164cd2a43aec2dedf4109131b1", + "0x009a05037883da4556d1eb804b43c05fba7d961bbf77d48b06d4fd4b986159f6095ab3af585bcb3df9060b1651da2360891a221de0d9325c04a49d8caa0cd800", + "0x06cdca5c9cced457b657b1af944d068a8ab962ef5fe08550778921a429c5bb2f106bd517f2778b534d455f1d780e8d823d918499b35488788a81546c22a2b257", + "0x04c7934d9f58f8f85be28784af049898b132dc5e80f4e96d294dcfc883736c430c2a97661da9e1fcd930e97c6184379e9a8c99ba72bb3f941a542df11ca481d6", + "0x00000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002a00324558eb3216bfae60f436ae4f80653125d6783123282af0eaa3766492ac1c012023ca7988684c6679a91abc62dbf0a5f49f4a4468e7c4c2e6de9bedce00864", + "0x000000000000000000000000000000000000000000000000000000000000004d0000000000000000000000000000000000000000000000000000000000000277034ca60d4657a94b25f98d458f8c879b4a67d24bb34650b9ade6cb0e0a4b6847043d8792aabcc5507963792b5efd5949aa034b1f784272b07eecfa5cc8b1b1d8" + ], + "value": "0x0000000000000000000000000000000000000000000000000000000000000183" + } + } + ] +} + "#; + + let proof = serde_json::from_str::(raw).unwrap(); + let account = verify::( + &new_mimc_constants_bls12_377(), + &proof.account_proof, + H256(hex!( + "0C76548458CC04A5AA09BFFA092B32C912AEE635C1C44364EBB911286A10263D" + )), + H160(hex!("5ff137d4b0fdcd49dca30c7cf57e578a026d2789")), + ) + .unwrap() + .unwrap(); + let value = verify::( + &new_mimc_constants_bls12_377(), + &proof.storage_proofs[0], + account.storage_root, + H256(hex!( + "975227e2a924779fb36829b74e9ab66f8d906444c0efb23059aaf437a9254f64" + )), + ) + .unwrap() + .unwrap(); + assert_eq!( + value, + hex!("0000000000000000000000000000000000000000000000000000000000000183").into() + ); + } + + #[test] + fn test_storage_noninclusion() { + let raw = r#" +{ + "accountProof": { + "key": "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", + "leafIndex": 65362, + "proof": { + "proofRelatedNodes": [ + "0x00000000000000000000000000000000000000000000000000000000000120fe0393507c456718a986386c7923fe68b87c29d83ac7f7ce1cdb49afc7e66a4771", + "0x008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809", + "0x060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b", + "0x0a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef322550a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef32255", + "0x01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d", + "0x090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484", + "0x11c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f30611c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f306", + "0x07f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d4807f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d48", + "0x0f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd80f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd8", + "0x0cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e27390cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e2739", + "0x014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b", + "0x11c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca55411c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca554", + "0x1092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c221092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c22", + "0x0969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a890969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a89", + "0x079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368", + "0x004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458", + "0x0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd", + "0x0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c", + "0x0defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea80defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea8", + "0x0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b", + "0x1276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f51276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f5", + "0x02a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b202a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b2", + "0x070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978", + "0x0133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed10133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed1", + "0x09cbd26c486bc2217bce59337120283f655a7ba65075f98059249f471812d0480b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f84", + "0x0c5c4d122720c4d6e7866d9b6bc6171c6259be90095976b665406ccf2dc6a8950305d7ebd7da4f82f061632eb7ec0c3060f51af848661d479bb64003f0fc5342", + "0x0c4762f6af9f09a529e70f0b34b7afafe2bba8944eccdcdb95cf13e0ff00ab2209d8b650f132967dba1764abe34c3d446311503ba7712d5f474a6e159b085b5f", + "0x0c3474a51e2654aca28b15add106ac676d92b9416ee788ac0b88873e77a009660176016fa85f1ba2375f784c72fae85763e12018e3e781c306f97ad9f826a22e", + "0x108459110262f154aef2d43fce77d314a7ec867f0068987716ff51582847e498009a6be6c408befa4eb7e6141fd427a2ce6489d50bf5f9de6bde9e100ada3482", + "0x118d3c53f9a3ea556029e867af93e9b4450cbacdf4dff29859e399ae16468e5102cefeff18d2980c8a9253c4609506472ba4764ea99efa6324dacf34740d9f05", + "0x005d88c799974510f99c04afdaba0f6b8f62edd55d8d89910009e148385a72c30a8fac91e2023660e8ac50ff082578361ba0901b16fe691f9b78044cbf6d1c4b", + "0x106f788c7d5990bec78f6c9cadd15604c99a8f1d56c875d324bb5ece63d83f3606694c69c43303aa1c614d60ed8fc66838f368b134cfc1ab00b6c83b2b5b3c8c", + "0x0ba8fdb8888982dde981f8e2cc9177c8c3ce0607661e113604e436951776de9c0b9ec8fec4b0696c73e04fd6bee4aa345633d23ef0c6bc4e4bbcf757af2677f0", + "0x0f5ae90881ea3398fd1a14fb83babc2335dfc4e6298aada1d827042d67dea48f0dee0a62e8ff86baddb091105d845c862089fe2f1963cd3798d636035da4d518", + "0x03d41bdb96726bf7f745784e42eef043c8b797f788d9720e36e460502e14c9fb0923e0e0228d2fe8619e30581e3e225d4e99e0daa011e15ac34c28fa30ea2989", + "0x11335bc4bf8a15d8c116cbdfe74242e80c7f60ac1a614d00f99fb9e1148126930502f7b7740708503e3858bc6df707cf4a1a751bcef3f2a5eb6eff9d8efa5cf8", + "0x0d60d90907794deaabe1e532a128e17ac94ec30339f3e367bc9ecd0aa40fd8b6009f71be21f99f29acd62b42787c99e5192646f808306fff0960ef5cd9a5ac16", + "0x0a6fd861ba25def420f5503fbbc4e0de2e54b4fbf0b22364e4a188eaf72ac58c02e49a2a28faca35409f471b4d981951aeabba2f091a427a2e88c53d1c7eeed3", + "0x0a93ecccd90368342584da9a8623e89a7a71d36f1da58d9874d50c045587138b0476d671e749bd2cd45fe416e1409caa22863f8cebdf926920a9f68b150d92d7", + "0x0821de61351452c22cf6bdafcd85be9a8cb3c2ad0af51f871d44221575785f9d12200803e31923cc68d6c9b906876643688e3a7ccb21264f933028b060564e4d", + "0x0000000000000000000000000000000000000000000000000000000000001c1e000000000000000000000000000000000000000000000000000000000000a354000036e661469dd70081ada16334d16a4049a124e261cd93def5fff88f85afda01b285fb7d6e0c7e05505a348777221f3c9fb491bbbbea4853e62e93f415efe7", + "0x000000000000000000000000000000000000000000000000000000000000894100000000000000000000000000000000000000000000000000000000000002db104a10331d6a854148a10b11c19cf2abae0412c9909ecefca54adc135ee57a950481fe75941093272afb1f8f76353afad6b89b1c19c383b07730c6f160b59243" + ], + "value": "0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000017d7498da306d2911280c3481d8b1510e16062ffaa631812c3ca53639329c1577354f0e4cd850dde1255c33de5e8c499e72ca1f49352847124c0dbfc30d0374d4d5d5e7cddb83c7ac93c806e738300b5357ecdc2e971d6438d34d8e4e17b99b758b1f9cac91c8e700000000000000000000000000000000000000000000000000000000000005c89" + } + }, + "storageProofs": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000000", + "leftLeafIndex": 611, + "leftProof": { + "proofRelatedNodes": [ + "0x00000000000000000000000000000000000000000000000000000000000003a90c382f6158633dfaf5ea90b4b6aef05e0171d9c5e97a2f3aa41c3944e2d08f7c", + "0x008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809", + "0x060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b", + "0x0a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef322550a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef32255", + "0x01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d", + "0x090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484", + "0x11c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f30611c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f306", + "0x07f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d4807f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d48", + "0x0f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd80f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd8", + "0x0cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e27390cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e2739", + "0x014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b", + "0x11c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca55411c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca554", + "0x1092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c221092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c22", + "0x0969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a890969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a89", + "0x079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368", + "0x004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458", + "0x0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd", + "0x0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c", + "0x0defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea80defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea8", + "0x0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b", + "0x1276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f51276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f5", + "0x02a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b202a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b2", + "0x070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978", + "0x0133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed10133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed1", + "0x0b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f840b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f84", + "0x0f3f9cf1e5ba6bdbb6daafc405bcceac97270fe89265b6a0faa2ba4bfd5cbf5d0f3f9cf1e5ba6bdbb6daafc405bcceac97270fe89265b6a0faa2ba4bfd5cbf5d", + "0x08b60393196453ee74fdf240449d9aa2569875b43596ea2621eecda8d8909acd08b60393196453ee74fdf240449d9aa2569875b43596ea2621eecda8d8909acd", + "0x10c439d656480d21a08c068717556fb8104a7a76e26f60e393ce4e36ae21e07b10c439d656480d21a08c068717556fb8104a7a76e26f60e393ce4e36ae21e07b", + "0x09ea86c5cd59ac4bfca4e46e7b50bb37c8327350888ba71112ecf3f5093baaef09ea86c5cd59ac4bfca4e46e7b50bb37c8327350888ba71112ecf3f5093baaef", + "0x0b971345bfa43e192ca2fb1c9ddd19f2dddf461243b1a54fdd5a4d581f850c110b971345bfa43e192ca2fb1c9ddd19f2dddf461243b1a54fdd5a4d581f850c11", + "0x0edd0129edd35191a183ecd28cbcab2a48ad381215d8544acf35248639835dcd0edd0129edd35191a183ecd28cbcab2a48ad381215d8544acf35248639835dcd", + "0x0f6516c2cee4cfd3c3453717d360474888ae7a5e3fbe9434c0434650b44c39200b019e3375b0a9280488dc8154335e83fcceb92a3bfb90fe1188c6a5c2723683", + "0x0222fa2cc1728b6faff164e31b8b0c778f8b7c98046f625475e5d6ecd888e6e70cae760c7b8690d543c0558b3913c59e22748013099c0942e3960bb41d9078b8", + "0x0fdfeacb22084128e246e1408b7975e3de40182d76d2e3b13e73a455231b6690010cdcdd77f54be0e2bb237ca3acbf30ce20fc53d03e1d377fe07a2657c6a452", + "0x0eb38bc6c6d2dc3fc678880bad7bec0061cecaad838094521d352a0727944a5b049c14259c25252cb097d973dadae2e9645731f101d159115ce8dd6a6bc8d57c", + "0x0e40da89d95f318c0e1f985a6554ed305c5f27f7cae7de3e07f41151e5a311f70ea08c7b543f2257955fa4e937c498ce21ce3f2a5bab5d4245638922c6cce06b", + "0x04a3b8e7b06e29a06e335bf80be7a2908997f3294a9472be89cd95b1288e70c709cc26dc8c3b431c560c3847fde0ba114ac1bf58e3894738b7a35f72bf53a7b5", + "0x005f762408388dc791d8064731ec0a4e6a256c69737f331b53f54d55308c87df0c7945adb1ac77e84bf94603a7e00de2dfc3c44d64d2ef5e5be63ae079fc15cc", + "0x0254769f3f328564163e0be11c364aa2b4b651a975397c18c4608206a00998a00d3527a52738cd568ff1954312aeefda2bca64e4d91eb964fc3a0da5dd1c2b46", + "0x0565ad1253bbaa5388ddb68dd52adb83a99c90a7a01f7037011c03727a2e1d3f0ba2a9a0599fad5ffb5fde06fc3368457d786ad2eabeb372cea6ed6481868094", + "0x00000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000002f1068da887b74fd30ff2e365193ddecf201afd9bd0181ffbc282939c943f0085d1120f168117038a271fcc94f9746eab5a6c682fa1efa41f23f6bacadedbc7b518", + "0x00000000000000000000000000000000000000000000000000000000000000a100000000000000000000000000000000000000000000000000000000000001ff0226bb24dc7fb5f8356e291c3ca45555a828a0e02bcb822a2878eadc51a11f1f066ef290cc3e13cdddabed678d6e64d13941dd6a0c6ed789f5774a99b90921f2" + ], + "value": "0x0000000000000000000000000000000000000000000000000000375911dbcbbc" + }, + "rightLeafIndex": 511, + "rightProof": { + "proofRelatedNodes": [ + "0x00000000000000000000000000000000000000000000000000000000000003a90c382f6158633dfaf5ea90b4b6aef05e0171d9c5e97a2f3aa41c3944e2d08f7c", + "0x008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809", + "0x060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b", + "0x0a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef322550a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef32255", + "0x01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d", + "0x090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484", + "0x11c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f30611c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f306", + "0x07f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d4807f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d48", + "0x0f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd80f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd8", + "0x0cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e27390cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e2739", + "0x014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b", + "0x11c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca55411c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca554", + "0x1092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c221092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c22", + "0x0969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a890969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a89", + "0x079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368", + "0x004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458", + "0x0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd", + "0x0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c", + "0x0defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea80defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea8", + "0x0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b", + "0x1276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f51276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f5", + "0x02a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b202a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b2", + "0x070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978", + "0x0133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed10133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed1", + "0x0b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f840b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f84", + "0x0f3f9cf1e5ba6bdbb6daafc405bcceac97270fe89265b6a0faa2ba4bfd5cbf5d0f3f9cf1e5ba6bdbb6daafc405bcceac97270fe89265b6a0faa2ba4bfd5cbf5d", + "0x08b60393196453ee74fdf240449d9aa2569875b43596ea2621eecda8d8909acd08b60393196453ee74fdf240449d9aa2569875b43596ea2621eecda8d8909acd", + "0x10c439d656480d21a08c068717556fb8104a7a76e26f60e393ce4e36ae21e07b10c439d656480d21a08c068717556fb8104a7a76e26f60e393ce4e36ae21e07b", + "0x09ea86c5cd59ac4bfca4e46e7b50bb37c8327350888ba71112ecf3f5093baaef09ea86c5cd59ac4bfca4e46e7b50bb37c8327350888ba71112ecf3f5093baaef", + "0x0b971345bfa43e192ca2fb1c9ddd19f2dddf461243b1a54fdd5a4d581f850c110b971345bfa43e192ca2fb1c9ddd19f2dddf461243b1a54fdd5a4d581f850c11", + "0x0edd0129edd35191a183ecd28cbcab2a48ad381215d8544acf35248639835dcd0edd0129edd35191a183ecd28cbcab2a48ad381215d8544acf35248639835dcd", + "0x0d052b80abb809f9120c6b9884fffd52dd230a8dea0e503ee37a657412f956e4124085568263d79db22e8138cdfcddb82217762c26573f47a99464a1891998c0", + "0x057a6e9128039b33095b2f3a29a0a4fd749c71bbdf024304b2979061ef696fda02609e7431e6a45621b7f5a2208d7b2fb036e64f1302666b157de4b082633069", + "0x037b1a185403907b8636d653feaa7b9ad7a5d84a510bda3c0a12bc9472a4e5720c08e5c3f0f216918297ea9b1d23483c6c013f440f0068b25302c6d49fbf446a", + "0x0d3421780fcce001fa7b4b27f5e39c220f05d0ffc491d8db6c6b5bcb4bcfb6a90298723fb96997c79f8bd6801f7d331234a09d6516ece5efba060a4ae6bcbf48", + "0x0c276aa23e1ae6fdc99c3cc16f5ca012b50c616fb0684853e3bdad0379bc05fd0e65e9874dec3ac06a76fb1975a6758236c27541abb8806cf7e461e39dcae2b9", + "0x0e7f129f0ed133acf079202b62b76e9c1f090c38369877039a41f389cfb28f04120861f2c2857a0ae1efbb7c5bbdf831a16e3648c3173bc35164ede64dfbf264", + "0x00132e22083f4c5cd6faf3ed72a15db28e32d9ca043a0af318c4761b9fff9b8506f131ac26d2cba71ff6f3a4690468563bb280e426224b29627006c92b30a4a1", + "0x07ef4499f02012217d7ba74661d161165ba8ce341eb8feedc6fb91c9d3daf6ea022140824283e42f43fc52f0fa23057ce4869a8c5ca5a263b08257011df91fbc", + "0x10d1e689f780d25322ea063b70d009e4ecf8d2e24044e61849b526e5c92045780f386aa614b62d8f207b4aa98af1f28d4ed491ac4e371aa51aa2bb6bb7b9d2b9", + "0x000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000019d128c9ea07e20e3771e0d5c074ab0d250cf02502f2fcf253c3e627819053d8062063bfbb79af7fd4d7834a275cad3d9ccef2d5ade138040201e4b0533f5360ad6", + "0x000000000000000000000000000000000000000000000000000000000000026300000000000000000000000000000000000000000000000000000000000001840233297165af3cab341e7e30b38dc8bf19d538e0ab6c6a842b2acbf536027b150e43b52047962596b0ecab1ed42e1774bd419bd21323899e4afd25bb6635bd52" + ], + "value": "0x000000000000000000000000000000000000000000000000000000000000007d" + } + } + ] +} + "#; + + let proof = serde_json::from_str::(raw).unwrap(); + let account = verify::( + &new_mimc_constants_bls12_377(), + &proof.account_proof, + H256(hex!( + "0C76548458CC04A5AA09BFFA092B32C912AEE635C1C44364EBB911286A10263D" + )), + H160(hex!("5ff137d4b0fdcd49dca30c7cf57e578a026d2789")), + ) + .unwrap() + .unwrap(); + let value = verify::( + &new_mimc_constants_bls12_377(), + &proof.storage_proofs[0], + account.storage_root, + H256::default(), + ) + .unwrap(); + assert_eq!(value, None); + } + + #[test] + fn test_account_noninclusion() { + let raw = r#" +{ + "accountProof": { + "key": "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2780", + "leftLeafIndex": 50915, + "leftProof": { + "proofRelatedNodes": [ + "0x00000000000000000000000000000000000000000000000000000000000120fe0393507c456718a986386c7923fe68b87c29d83ac7f7ce1cdb49afc7e66a4771", + "0x008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809", + "0x060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b", + "0x0a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef322550a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef32255", + "0x01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d", + "0x090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484", + "0x11c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f30611c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f306", + "0x07f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d4807f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d48", + "0x0f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd80f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd8", + "0x0cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e27390cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e2739", + "0x014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b", + "0x11c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca55411c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca554", + "0x1092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c221092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c22", + "0x0969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a890969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a89", + "0x079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368", + "0x004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458", + "0x0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd", + "0x0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c", + "0x0defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea80defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea8", + "0x0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b", + "0x1276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f51276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f5", + "0x02a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b202a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b2", + "0x070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978", + "0x0133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed10133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed1", + "0x09cbd26c486bc2217bce59337120283f655a7ba65075f98059249f471812d0480b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f84", + "0x0c5c4d122720c4d6e7866d9b6bc6171c6259be90095976b665406ccf2dc6a8950305d7ebd7da4f82f061632eb7ec0c3060f51af848661d479bb64003f0fc5342", + "0x0c4762f6af9f09a529e70f0b34b7afafe2bba8944eccdcdb95cf13e0ff00ab2209d8b650f132967dba1764abe34c3d446311503ba7712d5f474a6e159b085b5f", + "0x125e0b74c9f944431f57100ababee80095eff47b54bcbeb59790ccab72af8a8d0ad6bd567eb864b9e5ce168bf3a7c87ee3963bc68577cb40d2c5118e69b02449", + "0x0ced56af58b33a68e2b2491f0e45eb9aeccf95cdee3692dcc6d22055ffc07c67034599ba21f7c7c190d1a3df45d61faf51eba99d40671bafb8a9f0987c1379a0", + "0x0cf419b794abebf2631eb9b71850e4720d5832926808fb04811b468cc34059e7023a96fe63cb2a1237ab59f91c368dba466d9670461b9f6177099aaaaab77479", + "0x0651d5368092407b9dcd8f50f62c8e2d7a3ceac68c83bfb4c1c6a72ddf919519126f610f1b0dcc4004d4e591f227faff14211aea80ff74e65e197c8c276f3dd3", + "0x0349cf762365c0272e1bbcdc42e189fb98f0bb81949c8f74af78d54734b393dd0898977dd60d5255f6d179d9c118a9715d9b8f179a76c92cfa4b50583086d174", + "0x04ad89f67466c52ab58ce2b607f1b8444feec75c301f43f0c9a20b792059e48f02d966dd372e7021d97187b428f14de7d8f9b016c6082ed9c032f504b934de7c", + "0x0402473cd6b64ef02e23ffde16a97698dd9f6e79c3922bb06169504cb13d259302e3e785b26b76f90b834588317d7efe81c21f8f6e127fc341e13f9173d2d33e", + "0x0bfaf269c21ddbe687cf0a56120f9da606cff565c418ca081d9523cefa5ee4900d219e305070c0a5ffda3b2d20799b3876e4f899f21e790c5e61607e0a33cda1", + "0x08eb97abb2df2a1affcd757cdd20fd0777e53470a38818e8febdae4236744efd0c3bee7aa429be1836b93f6871cff32c80888251602cb6782d7efbcfa96187f7", + "0x0d147eadaed99af1df14632babd90117d3b6927af8f22902ffec8c7e0c81ac7310ee5b6b5b5b359bdb76271dcbf28e006f4c93c024175751fd3628dd1e6f1be9", + "0x1130fa8251e74fc2eb80e836e988bdcd156d4bca6b3a8c118ed5e17ac2b926220883f050c04011d5437f4bf78454f01e24974a3acde10dab94ba8a7ee09c5cbd", + "0x0043381a4fb2920cae78e46cd412a078e4905f2ae436b3233aa162b15b3a6f2c12252dfbb99f31c4713a63abff2141b357f20eef57cbdb1c7687c1d3f64878c6", + "0x0a064a4646cb7f9fd9b3dcecb1fd8cbd1f2b859d97b56486ea002d285477346d03569d58aa002050c477fb037ca9564212f71476982ea550693529862d31579f", + "0x0000000000000000000000000000000000000000000000000000000000005d44000000000000000000000000000000000000000000000000000000000000ce6212214e893b51fb4bff3f919f49f5e3d98f9e90a0744098760f05ea5c0ad37e0b02ada81e47c1223c7d15ba45be0b16ee69bd6a5e2c26de3f2facb725db2a2563", + "0x00000000000000000000000000000000000000000000000000000000000088190000000000000000000000000000000000000000000000000000000000008cf50213dedcc29d8a1353bafac93de02055eb9fa839071f1a6257c8c1ed608ec01010a5194238ff342de60302f1918116fe22fd005173786d19a588d90a7bc82278" + ], + "value": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000007977874126658098c066972282d4c85f230520af3847e297fe7524f976873e50134373b65f439c874734ff51ea349327c140cde2e47a933146e6f9f2ad8eb17c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4700000000000000000000000000000000000000000000000000000000000000000" + }, + "rightLeafIndex": 36085, + "rightProof": { + "proofRelatedNodes": [ + "0x00000000000000000000000000000000000000000000000000000000000120fe0393507c456718a986386c7923fe68b87c29d83ac7f7ce1cdb49afc7e66a4771", + "0x008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809008a47a2a53dd5183a2dc127c399a004e2a6c7e60f73e104d7d79e6a2bd7e809", + "0x060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b060f08aed06ffb90efc9705dc38d37a7000da1add99cef1b8a84b9e72e7c8b7b", + "0x0a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef322550a06dc31ae8e893bca0a076decb8c0caa9036b5f394abf79d7956411eef32255", + "0x01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d01f35ef342eaa841ee4306d38f2a1adeafe8967d23c31fe1a379b9a69353da6d", + "0x090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484090d53176fd185da729d0d68e0c0e646ef148f15864685f4ba56be7b7cbb2484", + "0x11c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f30611c8e229e3e2ae40a4959e036d500753aaedb52cda67d9caf60f0629f0b4f306", + "0x07f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d4807f048ac696418580a55a864a10ed030871fd615d5ab460c54d6184c16441d48", + "0x0f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd80f5dc218160db17cfe8044d7ac4fd55dfcbdf2676815e2c15388f189bf144cd8", + "0x0cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e27390cdf7d06a4b4b0e71713048f5f6ea86016467e909a27bfeeeca67b56c17e2739", + "0x014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b014030b5cbe31660da2d33b6b1265b82bbde9a7ab7f331f8b274f2b798a45a3b", + "0x11c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca55411c8aeb3dc3ca059a29ba20d4471b20987d74a0d79ff8ecda247df6a02eca554", + "0x1092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c221092d1b2349c4fbc88ea0202cf88685e4e316c99697063f786201b27d46e2c22", + "0x0969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a890969f4e85b86f0eb36ad13dfb1f35346d7d6518308dc27e73452c649850f1a89", + "0x079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368079081f446c9a0c7b404834742cea1909426ccfc4696d19e1a08531b0cc30368", + "0x004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458004d50e626bda007887a31f60883e58bce50a1a3e7a3384b9ec18dab319dd458", + "0x0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd0b2ae68e3af633dac72090cc9c9b0dce76cebf5117101a265f54b3b9a851b3cd", + "0x0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c0b7a8a9fe0ee619c9bd7ff504dcb47bdce0193546b53a79dedd5251f4f56f36c", + "0x0defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea80defe934a1ae079cf6ec6022145b60128eeb30503eea4404da990fc2b2430ea8", + "0x0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b0e42718d49cb8c4be515181eda51f41d3b8198af5a2139a4670a8ee06b904a2b", + "0x1276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f51276c046afd611be02a66cf85498d7210a15293357afe07968a86c89356662f5", + "0x02a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b202a9fd706c3c223f9374481b7495fb775c1675407556d93f1edabfe54b3fc9b2", + "0x070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978070382f72e9f322433fb44fc4acfefd74b277b19b6cc1784379e7ca7338a2978", + "0x0133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed10133209cd7936e208da6b743428ff7195e8ef92d3dac72472146ac7497355ed1", + "0x09cbd26c486bc2217bce59337120283f655a7ba65075f98059249f471812d0480b03678742039acaae14fd3964e2d6261b74410043c536f07bcf1bc4495d9f84", + "0x0c5c4d122720c4d6e7866d9b6bc6171c6259be90095976b665406ccf2dc6a8950305d7ebd7da4f82f061632eb7ec0c3060f51af848661d479bb64003f0fc5342", + "0x0bd67fb37adfe69856936db64ba99e14abd6a73124332d622937c0e27697c6ce023c0ebe791b9fedd67c5d6bcb60b7e8f1b34a8bc3212626520e7ef92de490d2", + "0x115b79d9b04cd131a492cba8892fe3768ffc4d7e6ba4883648bb735cdc98231a11db4ddd2d019276c2bf4b5a63b25d69e43ade93e229401fe636c8d2b0abfcd2", + "0x036a0c38dc37c9dd30b3dd485a7a61e9c55cc6db0ab664122d93f566b40b642f0f71a7ca8aea7890f183d07c6bba517ac6132f7bb188f07ec32ca33cda9ddb9d", + "0x126841c8020009187e5b49a59787a2a297227db1e11b5a78a1cace93cc4ce09c116ee798f5d5acf0b5df94de55ecb6ca1d638228746d2cdeed3c5c187fc3bd4e", + "0x05d430417e93bbd414de5f2c529407b97eb843cc083d8105a2a6ec0868abc27004f133f13f993f98f15c3cf3e1eff101113834c763234bc8e1896ec3861063b2", + "0x10530f986ec1a17af8fef6d2fbbb901accfd9d60828c6c0f6a6da1e926f948790f05a301b210f45406769f0ac2c45748da6dccce762d4adcc5dc85de4990f2c1", + "0x0284a0134f5ac9cd8acfa1c79ca9f035f9728e5de39373e9ddca53625bd1ed280aa5ae2f615ab38828700102da3413de7a96d8878f5f097d09063fe6e7f0a12b", + "0x0f7e46da1c787eb19b7788a82848f478e82bc2ac3575f90426d75c72f479c7ec1268704b94d6bd2a1a6d8ffbf27f433a36aca5a5fa661c6187e2e4b8cbc848a9", + "0x06c9e4b29458dde1213ff22ed7452f39b8c70b7ef314c5a7949838837a48ea4808291f2d7f0133f66b8b23b06e8a5f6c4a292cea5bb4dcf4081c49cabd7d6c66", + "0x10ab620750e513b9bc4a14270f8e7ea8e3e7d2187bbf37af175ae65a69b2b3a80584348d8fe95db0d10ec360e4d5e09f789f3021fc250e9e24977413fb500333", + "0x0c185bc72eb982991d63be5c18a36298fccea38d8ad1363e54e01ae41ef6885909a48e907f9e6574b0d2f29a8e35ef2bccfca4316a03b666539b6f795b8ec08f", + "0x0a42d55940760dbd55a4bf2e987f202b63291dc230115362fae168c00027b6ec0b838ec78059b519c8d17d9b4748fef69886684d209d3583e29261e6f9d7a7b7", + "0x0f909e7857d476f194af529bcc51ef515960148352cae9b4e2a4292bd207bcf01218b51c9c9be2c5f66829c972eeae0d473d16770955718d50add1ed7ac1d4f2", + "0x127c1c643468d9bc6fed375cfce594c169c5855b2bc19bd70818ee4d1e4ac6d812831b628352e7c7064f6d3440f9e2d03fa3336702edba27aea748ddff0478f9", + "0x0000000000000000000000000000000000000000000000000000000000008de60000000000000000000000000000000000000000000000000000000000004ff211d6fa740eb934a32b9e49dd7f2f655eb5c8a8014b36ad55b0cd66bae1b7b3a810a5194238ff342de60302f1918116fe22fd005173786d19a588d90a7bc82278", + "0x000000000000000000000000000000000000000000000000000000000000c6e3000000000000000000000000000000000000000000000000000000000000cf420213ef7ebf37eca24c05838ef0597f9aac9e9ba9dd49e75012ab573c8b8c3f3910a5194238ff342de60302f1918116fe22fd005173786d19a588d90a7bc82278" + ], + "value": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000007977874126658098c066972282d4c85f230520af3847e297fe7524f976873e50134373b65f439c874734ff51ea349327c140cde2e47a933146e6f9f2ad8eb17c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4700000000000000000000000000000000000000000000000000000000000000000" + } + }, + "storageProofs": [] +} + "#; + + let proof = serde_json::from_str::(raw).unwrap(); + let account = verify::( + &new_mimc_constants_bls12_377(), + &proof.account_proof, + H256(hex!( + "0C76548458CC04A5AA09BFFA092B32C912AEE635C1C44364EBB911286A10263D" + )), + H160(hex!("5ff137d4b0fdcd49dca30c7cf57e578a026d2780")), + ) + .unwrap(); + assert_eq!(account, None); + } + + // https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/core/src/test/java/net/consensys/shomei/ZkAccountTest.java#L34 + #[test] + fn test_hash_zeroaccount() { + let mimc_constants = new_mimc_constants_bls12_377(); + let mimc = new_mimc_bls12_377(&mimc_constants); + let hash = mimc + .update(&ZkAccount::default().into_bytes()) + .unwrap() + .finalize(); + assert_eq!( + hash, + hex!("0f170eaef9275fd6098a06790c63a141e206e0520738a4cf5cf5081d495e8682") + ); + } + + #[test] + fn test_hashed_key() { + let mimc_constants = new_mimc_constants_bls12_377(); + let mimc = new_mimc_bls12_377(&mimc_constants); + let hash = mimc + .update(hex!( + "0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789" + )) + .unwrap() + .finalize(); + assert_eq!( + H256(hex!( + "104a10331d6a854148a10b11c19cf2abae0412c9909ecefca54adc135ee57a95" + )), + hash.try_into().unwrap(), + ); + } +} diff --git a/lib/scroll-verifier/src/verify.rs b/lib/scroll-verifier/src/verify.rs index 57acce8ba4..f61a3f8ff4 100644 --- a/lib/scroll-verifier/src/verify.rs +++ b/lib/scroll-verifier/src/verify.rs @@ -212,7 +212,7 @@ mod tests { let proof: Proof = serde_json::from_str(&std::fs::read_to_string("tests/scroll_proof.json").unwrap()) .unwrap(); - assert!(matches!( + assert_eq!( verify_zktrie_storage_proof( H256(hex!( "1b52888cae05bdba27f8470293a7d2bc3b9a9c822d96affe05ef243e0dfd44a0" @@ -222,7 +222,7 @@ mod tests { &proof.proof ), Ok(()) - )) + ) } #[test] @@ -230,7 +230,7 @@ mod tests { let proof: Proof = serde_json::from_str(&std::fs::read_to_string("tests/scroll_absent.json").unwrap()) .unwrap(); - assert!(matches!( + assert_eq!( verify_zktrie_storage_absence( H256(hex!( "1b52888cae05bdba27f8470293a7d2bc3b9a9c822d96affe05ef243e0dfd44a0" @@ -239,6 +239,6 @@ mod tests { &proof.proof ), Ok(()) - )) + ) } } diff --git a/lib/unionlabs/src/ibc/lightclients.rs b/lib/unionlabs/src/ibc/lightclients.rs index 1c30d8982e..23dfaa3d5d 100644 --- a/lib/unionlabs/src/ibc/lightclients.rs +++ b/lib/unionlabs/src/ibc/lightclients.rs @@ -1,5 +1,6 @@ pub mod cometbls; pub mod ethereum; +pub mod linea; pub mod scroll; pub mod tendermint; pub mod wasm; diff --git a/lib/unionlabs/src/ibc/lightclients/linea.rs b/lib/unionlabs/src/ibc/lightclients/linea.rs new file mode 100644 index 0000000000..1e80278bf9 --- /dev/null +++ b/lib/unionlabs/src/ibc/lightclients/linea.rs @@ -0,0 +1,3 @@ +pub mod client_state; +pub mod consensus_state; +pub mod header; diff --git a/lib/unionlabs/src/ibc/lightclients/linea/client_state.rs b/lib/unionlabs/src/ibc/lightclients/linea/client_state.rs new file mode 100644 index 0000000000..69bc6df877 --- /dev/null +++ b/lib/unionlabs/src/ibc/lightclients/linea/client_state.rs @@ -0,0 +1,120 @@ +use core::{fmt::Debug, str::FromStr}; + +use macros::model; +use uint::FromDecStrErr; + +use crate::{ + errors::{required, InvalidLength, MissingField}, + hash::H160, + ibc::core::client::height::Height, + uint::U256, +}; + +#[model(proto( + raw(protos::union::ibc::lightclients::linea::v1::ClientState), + into, + from +))] +pub struct ClientState { + pub chain_id: U256, + // TODO: This should be ClientId + pub l1_client_id: String, + pub l1_latest_height: Height, + pub l1_rollup_contract_address: H160, + pub l1_rollup_current_l2_block_number_slot: U256, + pub l1_rollup_current_l2_timestamp_slot: U256, + pub l1_rollup_l2_state_root_hashes_slot: U256, + pub l2_ibc_contract_address: H160, + pub l2_ibc_contract_commitment_slot: U256, + pub frozen_height: Height, +} + +impl From for protos::union::ibc::lightclients::linea::v1::ClientState { + fn from(value: ClientState) -> Self { + Self { + chain_id: value.chain_id.to_string(), + l1_client_id: value.l1_client_id, + l1_latest_height: Some(value.l1_latest_height.into()), + l1_rollup_contract_address: value.l1_rollup_contract_address.into(), + l1_rollup_current_l2_block_number_slot: value + .l1_rollup_current_l2_block_number_slot + .to_be_bytes() + .to_vec(), + l1_rollup_current_l2_timestamp_slot: value + .l1_rollup_current_l2_timestamp_slot + .to_be_bytes() + .to_vec(), + l1_rollup_l2_state_root_hashes_slot: value + .l1_rollup_l2_state_root_hashes_slot + .to_be_bytes() + .to_vec(), + l2_ibc_contract_address: value.l2_ibc_contract_address.into(), + l2_ibc_contract_commitment_slot: value + .l2_ibc_contract_commitment_slot + .to_be_bytes() + .into(), + frozen_height: Some(value.frozen_height.into()), + } + } +} + +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum TryFromClientStateError { + #[error("unable to parse chain id")] + ChainId(#[source] FromDecStrErr), + #[error(transparent)] + MissingField(MissingField), + #[error("invalid l1 latest height")] + L1LatestHeight, + #[error("invalid rollup contract address")] + L1RollupContractAddress(#[source] InvalidLength), + #[error("invalid rollup current_l2_block_number slot")] + L1RollupCurrentL2BlockNumberSlot(#[source] InvalidLength), + #[error("invalid rollup current_l2_timestamp slot")] + L1RollupCurrentL2TimestampSlot(#[source] InvalidLength), + #[error("invalid rollup l2_state_roots mapping slot")] + L1RollupL2StateRootHashesSlot(#[source] InvalidLength), + #[error("invalid l2 ibc contract address")] + L2IbcContractAddress(#[source] InvalidLength), + #[error("invalid l2 ibc commitment slot")] + L2IbcContractCommitmentSlot(#[source] InvalidLength), +} + +impl TryFrom for ClientState { + type Error = TryFromClientStateError; + + fn try_from( + value: protos::union::ibc::lightclients::linea::v1::ClientState, + ) -> Result { + Ok(Self { + l1_client_id: value.l1_client_id, + chain_id: U256::from_str(&value.chain_id).map_err(TryFromClientStateError::ChainId)?, + l1_latest_height: required!(value.l1_latest_height)?.into(), + l1_rollup_contract_address: value + .l1_rollup_contract_address + .try_into() + .map_err(TryFromClientStateError::L1RollupContractAddress)?, + l1_rollup_current_l2_block_number_slot: U256::try_from_be_bytes( + &value.l1_rollup_current_l2_block_number_slot, + ) + .map_err(TryFromClientStateError::L1RollupCurrentL2BlockNumberSlot)?, + l1_rollup_current_l2_timestamp_slot: U256::try_from_be_bytes( + &value.l1_rollup_current_l2_timestamp_slot, + ) + .map_err(TryFromClientStateError::L1RollupCurrentL2TimestampSlot)?, + l1_rollup_l2_state_root_hashes_slot: U256::try_from_be_bytes( + &value.l1_rollup_l2_state_root_hashes_slot, + ) + .map_err(TryFromClientStateError::L1RollupL2StateRootHashesSlot)?, + l2_ibc_contract_address: value + .l2_ibc_contract_address + .try_into() + .map_err(TryFromClientStateError::L2IbcContractAddress)?, + l2_ibc_contract_commitment_slot: U256::try_from_be_bytes( + &value.l2_ibc_contract_commitment_slot, + ) + .map_err(TryFromClientStateError::L2IbcContractCommitmentSlot)?, + frozen_height: value.frozen_height.unwrap_or_default().into(), + }) + } +} diff --git a/lib/unionlabs/src/ibc/lightclients/linea/consensus_state.rs b/lib/unionlabs/src/ibc/lightclients/linea/consensus_state.rs new file mode 100644 index 0000000000..864f6c12e3 --- /dev/null +++ b/lib/unionlabs/src/ibc/lightclients/linea/consensus_state.rs @@ -0,0 +1,44 @@ +use macros::model; + +use crate::{errors::InvalidLength, hash::H256}; + +#[model(proto( + raw(protos::union::ibc::lightclients::linea::v1::ConsensusState), + into, + from +))] +pub struct ConsensusState { + pub ibc_storage_root: H256, + pub timestamp: u64, +} + +impl From for protos::union::ibc::lightclients::linea::v1::ConsensusState { + fn from(value: ConsensusState) -> Self { + Self { + ibc_storage_root: value.ibc_storage_root.into(), + timestamp: value.timestamp, + } + } +} + +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum TryFromConsensusStateError { + #[error("invalid ibc storage root")] + IbcStorageRoot(#[source] InvalidLength), +} + +impl TryFrom for ConsensusState { + type Error = TryFromConsensusStateError; + + fn try_from( + value: protos::union::ibc::lightclients::linea::v1::ConsensusState, + ) -> Result { + Ok(Self { + ibc_storage_root: value + .ibc_storage_root + .try_into() + .map_err(TryFromConsensusStateError::IbcStorageRoot)?, + timestamp: value.timestamp, + }) + } +} diff --git a/lib/unionlabs/src/ibc/lightclients/linea/header.rs b/lib/unionlabs/src/ibc/lightclients/linea/header.rs new file mode 100644 index 0000000000..eef401f872 --- /dev/null +++ b/lib/unionlabs/src/ibc/lightclients/linea/header.rs @@ -0,0 +1,89 @@ +use macros::model; + +use crate::{ + errors::{required, InvalidLength, MissingField}, + hash::H256, + ibc::{ + core::client::height::Height, + lightclients::ethereum::{ + account_proof::{AccountProof, TryFromAccountProofError}, + storage_proof::{StorageProof, TryFromStorageProofError}, + }, + }, + linea::proof::{MerkleProof, TryFromMerkleProofError}, +}; + +#[model(proto(raw(protos::union::ibc::lightclients::linea::v1::Header), into, from))] +pub struct Header { + pub l1_height: Height, + pub l1_rollup_contract_proof: AccountProof, + pub l2_block_number: u64, + pub l2_block_number_proof: StorageProof, + pub l2_state_root: H256, + pub l2_state_root_proof: StorageProof, + pub l2_timestamp: u64, + pub l2_timestamp_proof: StorageProof, + pub l2_ibc_contract_proof: MerkleProof, +} + +impl From
for protos::union::ibc::lightclients::linea::v1::Header { + fn from(value: Header) -> Self { + Self { + l1_height: Some(value.l1_height.into()), + l1_rollup_contract_proof: Some(value.l1_rollup_contract_proof.into()), + l2_block_number: value.l2_block_number, + l2_block_number_proof: Some(value.l2_block_number_proof.into()), + l2_state_root: value.l2_state_root.into(), + l2_state_root_proof: Some(value.l2_state_root_proof.into()), + l2_timestamp: value.l2_timestamp, + l2_timestamp_proof: Some(value.l2_timestamp_proof.into()), + l2_ibc_contract_proof: Some(value.l2_ibc_contract_proof.into()), + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub enum TryFromHeaderError { + MissingField(MissingField), + L1RollupContractProof(TryFromAccountProofError), + L2BlockNumber(InvalidLength), + L2BlockNumberProof(TryFromStorageProofError), + L2StateRoot(InvalidLength), + L2StateRootProof(TryFromStorageProofError), + L2Timestamp(InvalidLength), + L2TimestampProof(TryFromStorageProofError), + L2IbcContractProof(TryFromMerkleProofError), +} + +impl TryFrom for Header { + type Error = TryFromHeaderError; + + fn try_from( + value: protos::union::ibc::lightclients::linea::v1::Header, + ) -> Result { + Ok(Self { + l1_height: required!(value.l1_height)?.into(), + l1_rollup_contract_proof: required!(value.l1_rollup_contract_proof)? + .try_into() + .map_err(TryFromHeaderError::L1RollupContractProof)?, + l2_block_number: value.l2_block_number, + l2_block_number_proof: required!(value.l2_block_number_proof)? + .try_into() + .map_err(TryFromHeaderError::L2BlockNumberProof)?, + l2_state_root: value + .l2_state_root + .try_into() + .map_err(TryFromHeaderError::L2StateRoot)?, + l2_state_root_proof: required!(value.l2_state_root_proof)? + .try_into() + .map_err(TryFromHeaderError::L2StateRootProof)?, + l2_timestamp: value.l2_timestamp, + l2_timestamp_proof: required!(value.l2_timestamp_proof)? + .try_into() + .map_err(TryFromHeaderError::L2TimestampProof)?, + l2_ibc_contract_proof: required!(value.l2_ibc_contract_proof)? + .try_into() + .map_err(TryFromHeaderError::L2IbcContractProof)?, + }) + } +} diff --git a/lib/unionlabs/src/lib.rs b/lib/unionlabs/src/lib.rs index a1eeb45b79..5ed9e83275 100644 --- a/lib/unionlabs/src/lib.rs +++ b/lib/unionlabs/src/lib.rs @@ -53,6 +53,9 @@ pub mod union; /// Types specific to the scroll protocol. pub mod scroll; +/// Types specific to the linea protocol. +pub mod linea; + /// Wrapper types around [`milagro_bls`] types, providing more conversions and a simpler signing interface. pub mod bls; @@ -141,6 +144,7 @@ pub enum WasmClientType { Cometbls, Tendermint, Scroll, + Linea, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/lib/unionlabs/src/linea.rs b/lib/unionlabs/src/linea.rs new file mode 100644 index 0000000000..3f90d3e748 --- /dev/null +++ b/lib/unionlabs/src/linea.rs @@ -0,0 +1,2 @@ +pub mod account; +pub mod proof; diff --git a/lib/unionlabs/src/linea/account.rs b/lib/unionlabs/src/linea/account.rs new file mode 100644 index 0000000000..4bacacbb79 --- /dev/null +++ b/lib/unionlabs/src/linea/account.rs @@ -0,0 +1,84 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + errors::{ExpectedLength, InvalidLength}, + hash::H256, + uint::U256, + ByteArrayExt, +}; + +pub const ZKACCOUNT_BYTES_LEN: usize = 32 * 6; + +// https://github.com/Consensys/shomei/blob/955b4d8100f1a12702cdefc3fa79b16dd1c038e6/core/src/main/java/net/consensys/shomei/ZkAccount.java +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct ZkAccount { + pub nonce: U256, + pub balance: U256, + pub storage_root: H256, + pub mimc_code_hash: H256, + pub keccak_code_hash: MimcSafeBytes, + pub code_size: U256, +} + +impl ZkAccount { + pub fn into_bytes(self) -> Vec { + self.into() + } +} + +impl Into> for ZkAccount { + fn into(self) -> Vec { + [ + self.nonce.to_be_bytes().as_ref(), + self.balance.to_be_bytes().as_ref(), + &self.storage_root.into_bytes(), + &self.mimc_code_hash.into_bytes(), + &self.keccak_code_hash.into_bytes(), + self.code_size.to_be_bytes().as_ref(), + ] + .concat() + } +} + +impl TryFrom<&[u8]> for ZkAccount { + type Error = InvalidLength; + fn try_from(value: &[u8]) -> Result { + let value = <[u8; ZKACCOUNT_BYTES_LEN]>::try_from(value).map_err(|_| InvalidLength { + expected: ExpectedLength::Exact(ZKACCOUNT_BYTES_LEN), + found: value.len(), + })?; + Ok(ZkAccount { + nonce: U256::from_be_bytes(value.array_slice::<0, 32>()), + balance: U256::from_be_bytes(value.array_slice::<32, 32>()), + storage_root: value.array_slice::<64, 32>().into(), + mimc_code_hash: value.array_slice::<96, 32>().into(), + keccak_code_hash: value.array_slice::<128, 32>().into(), + code_size: U256::from_be_bytes(value.array_slice::<160, 32>()), + }) + } +} + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct MimcSafeBytes { + pub lsb: H256, + pub msb: H256, +} + +impl MimcSafeBytes { + pub fn into_bytes(self) -> Vec { + [self.lsb.0, self.msb.0].concat() + } +} + +impl From<[u8; 32]> for MimcSafeBytes { + fn from(value: [u8; 32]) -> Self { + let mut lsb = [0u8; 32]; + let mut msb = [0u8; 32]; + lsb[16..32].copy_from_slice(&value.array_slice::<16, 16>()); + msb[16..32].copy_from_slice(&value.array_slice::<0, 16>()); + Self { + lsb: lsb.into(), + msb: msb.into(), + } + } +} diff --git a/lib/unionlabs/src/linea/proof.rs b/lib/unionlabs/src/linea/proof.rs new file mode 100644 index 0000000000..927ff635d4 --- /dev/null +++ b/lib/unionlabs/src/linea/proof.rs @@ -0,0 +1,175 @@ +use macros::model; +use serde::{Deserialize, Serialize}; + +use crate::errors::{required, MissingField}; + +#[derive(Debug, PartialEq, Clone, thiserror::Error)] +pub enum TryFromMerkleProofError { + #[error(transparent)] + MissingField(#[from] MissingField), +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MerklePath { + #[serde(with = "::serde_utils::hex_string")] + pub value: Vec, + #[serde(with = "::serde_utils::hex_string_list")] + pub proof_related_nodes: Vec>, +} + +impl From for protos::union::ibc::lightclients::linea::v1::MerklePath { + fn from(value: MerklePath) -> Self { + Self { + value: value.value, + proof_related_nodes: value.proof_related_nodes, + } + } +} + +impl From for MerklePath { + fn from(value: protos::union::ibc::lightclients::linea::v1::MerklePath) -> Self { + Self { + value: value.value, + proof_related_nodes: value.proof_related_nodes, + } + } +} + +#[model( + proto( + raw(protos::union::ibc::lightclients::linea::v1::InclusionProof), + into, + from + ), + no_serde +)] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct InclusionProof { + #[serde(with = "::serde_utils::hex_string")] + pub key: Vec, + pub leaf_index: u64, + pub proof: MerklePath, +} + +impl From for protos::union::ibc::lightclients::linea::v1::InclusionProof { + fn from(value: InclusionProof) -> Self { + Self { + key: value.key, + leaf_index: value.leaf_index, + merkle_path: Some(value.proof.into()), + } + } +} + +impl TryFrom for InclusionProof { + type Error = TryFromMerkleProofError; + + fn try_from( + value: protos::union::ibc::lightclients::linea::v1::InclusionProof, + ) -> Result { + Ok(Self { + key: value.key, + leaf_index: value.leaf_index, + proof: required!(value.merkle_path)?.into(), + }) + } +} + +#[model( + proto( + raw(protos::union::ibc::lightclients::linea::v1::NonInclusionProof), + into, + from + ), + no_serde +)] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NonInclusionProof { + #[serde(with = "::serde_utils::hex_string")] + pub key: Vec, + pub left_leaf_index: u64, + pub left_proof: MerklePath, + pub right_leaf_index: u64, + pub right_proof: MerklePath, +} + +impl From for protos::union::ibc::lightclients::linea::v1::NonInclusionProof { + fn from(value: NonInclusionProof) -> Self { + Self { + key: value.key, + left_leaf_index: value.left_leaf_index, + left_proof: Some(value.left_proof.into()), + right_leaf_index: value.right_leaf_index, + right_proof: Some(value.right_proof.into()), + } + } +} + +impl TryFrom for NonInclusionProof { + type Error = TryFromMerkleProofError; + + fn try_from( + value: protos::union::ibc::lightclients::linea::v1::NonInclusionProof, + ) -> Result { + Ok(Self { + key: value.key, + left_leaf_index: value.left_leaf_index, + left_proof: required!(value.left_proof)?.into(), + right_leaf_index: value.right_leaf_index, + right_proof: required!(value.right_proof)?.into(), + }) + } +} + +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum MerkleProof { + Inclusion(InclusionProof), + NonInclusion(NonInclusionProof), +} + +impl From for protos::union::ibc::lightclients::linea::v1::MerkleProof { + fn from(value: MerkleProof) -> Self { + Self { + proof: Some(match value { + MerkleProof::Inclusion(inclusion) => { + protos::union::ibc::lightclients::linea::v1::merkle_proof::Proof::Inclusion( + inclusion.into(), + ) + } + MerkleProof::NonInclusion(noninclusion) => { + protos::union::ibc::lightclients::linea::v1::merkle_proof::Proof::Noninclusion( + noninclusion.into(), + ) + } + }), + } + } +} + +impl TryFrom for MerkleProof { + type Error = TryFromMerkleProofError; + + fn try_from( + value: protos::union::ibc::lightclients::linea::v1::MerkleProof, + ) -> Result { + Ok(match required!(value.proof)? { + protos::union::ibc::lightclients::linea::v1::merkle_proof::Proof::Inclusion( + inclusion, + ) => MerkleProof::Inclusion(inclusion.try_into()?), + protos::union::ibc::lightclients::linea::v1::merkle_proof::Proof::Noninclusion( + noninclusion, + ) => MerkleProof::NonInclusion(noninclusion.try_into()?), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetProof { + pub account_proof: MerkleProof, + pub storage_proofs: Vec, +} diff --git a/light-clients/linea-light-client/Cargo.toml b/light-clients/linea-light-client/Cargo.toml new file mode 100644 index 0000000000..561d350ec9 --- /dev/null +++ b/light-clients/linea-light-client/Cargo.toml @@ -0,0 +1,37 @@ +[package] +authors = ["Union Labs"] +edition = "2021" +license-file = { workspace = true } +name = "linea-light-client" +publish = false +version = "0.1.0" + +[lints] +workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cosmwasm-std = { workspace = true, features = ["abort"] } +ethereum-verifier = { workspace = true } +ethers-core.workspace = true +gnark-mimc = { workspace = true } +hex = { workspace = true } +ics008-wasm-client = { workspace = true } +linea-verifier = { workspace = true } +linea-zktrie = { workspace = true } +protos = { workspace = true } +rlp = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde-json-wasm = { workspace = true } +sha3 = { workspace = true } +thiserror = { workspace = true } +tiny-keccak = { workspace = true, features = ["keccak"] } +unionlabs = { workspace = true, features = ["ethabi", "stargate"] } + +[dev-dependencies] +base64 = { workspace = true } +hex = { workspace = true } +serde_json = { workspace = true } diff --git a/light-clients/linea-light-client/linea-light-client.nix b/light-clients/linea-light-client/linea-light-client.nix new file mode 100644 index 0000000000..0d21e7afd5 --- /dev/null +++ b/light-clients/linea-light-client/linea-light-client.nix @@ -0,0 +1,19 @@ +{ ... }: { + perSystem = { crane, lib, ensure-wasm-client-type, ... }: + let + workspace = (crane.buildWasmContract { + crateDirFromRoot = "light-clients/linea-light-client"; + checks = [ + (file_path: '' + ${ensure-wasm-client-type { + inherit file_path; + type = "Linea"; + }} + '') + ]; + }); + in + { + inherit (workspace) packages checks; + }; +} diff --git a/light-clients/linea-light-client/src/client.rs b/light-clients/linea-light-client/src/client.rs new file mode 100644 index 0000000000..3fac900f79 --- /dev/null +++ b/light-clients/linea-light-client/src/client.rs @@ -0,0 +1,350 @@ +use cosmwasm_std::{Deps, DepsMut, Env}; +use gnark_mimc::new_mimc_constants_bls12_377; +use ics008_wasm_client::{ + storage_utils::{ + read_client_state, read_consensus_state, save_client_state, save_consensus_state, + update_client_state, + }, + IbcClient, IbcClientError, Status, StorageState, +}; +use sha3::Digest; +use unionlabs::{ + cosmwasm::wasm::union::custom_query::{query_consensus_state, UnionCustomQuery}, + encoding::{Decode, EncodeAs, EthAbi, Proto}, + google::protobuf::any::Any, + hash::H256, + ibc::{ + core::{ + client::{genesis_metadata::GenesisMetadata, height::Height}, + commitment::merkle_path::MerklePath, + }, + lightclients::{ + cometbls, + linea::{client_state::ClientState, consensus_state::ConsensusState, header::Header}, + wasm, + }, + }, + ics24::Path, + linea::{ + account::ZkAccount, + proof::{InclusionProof, NonInclusionProof}, + }, + uint::U256, +}; + +use crate::{errors::Error, eth_encoding::generate_commitment_key}; + +type WasmClientState = unionlabs::ibc::lightclients::wasm::client_state::ClientState; +type WasmConsensusState = + unionlabs::ibc::lightclients::wasm::consensus_state::ConsensusState; +type WasmL1ConsensusState = unionlabs::ibc::lightclients::wasm::consensus_state::ConsensusState< + unionlabs::ibc::lightclients::ethereum::consensus_state::ConsensusState, +>; + +pub struct LineaLightClient; + +impl IbcClient for LineaLightClient { + type Error = Error; + + type CustomQuery = UnionCustomQuery; + + type Header = Header; + + type Misbehaviour = Header; + + type ClientState = ClientState; + + type ConsensusState = ConsensusState; + + type Encoding = Proto; + + fn verify_membership( + deps: Deps, + height: Height, + _delay_time_period: u64, + _delay_block_period: u64, + proof: Vec, + mut path: MerklePath, + value: ics008_wasm_client::StorageState, + ) -> Result<(), IbcClientError> { + let consensus_state: WasmConsensusState = + read_consensus_state(deps, &height)?.ok_or(Error::ConsensusStateNotFound(height))?; + let client_state: WasmClientState = read_client_state(deps)?; + + let path = path.key_path.pop().ok_or(Error::EmptyIbcPath)?; + + // This storage root is verified during the header update, so we don't need to verify it again. + let storage_root = consensus_state.data.ibc_storage_root; + + match value { + StorageState::Occupied(value) => { + let inclusion_proof = + InclusionProof::decode(&proof).map_err(Error::StorageProofDecode)?; + do_verify_membership( + path, + storage_root, + client_state.data.l2_ibc_contract_commitment_slot, + inclusion_proof, + value, + )? + } + StorageState::Empty => { + let noninclusion_proof = + NonInclusionProof::decode(&proof).map_err(Error::StorageProofDecode)?; + do_verify_non_membership( + path, + storage_root, + client_state.data.l2_ibc_contract_commitment_slot, + noninclusion_proof, + )? + } + } + + Ok(()) + } + + fn verify_header( + deps: Deps, + env: Env, + header: Self::Header, + ) -> Result<(), IbcClientError> { + let client_state: WasmClientState = read_client_state(deps)?; + let l1_consensus_state = query_consensus_state::( + deps, + &env, + client_state.data.l1_client_id.clone(), + header.l1_height, + ) + .map_err(Error::CustomQuery)?; + linea_verifier::verify_header( + client_state.data, + header, + l1_consensus_state.data.state_root, + ) + .map_err(Error::Verify)?; + Ok(()) + } + + fn verify_misbehaviour( + _deps: Deps, + _env: Env, + _misbehaviour: Self::Misbehaviour, + ) -> Result<(), IbcClientError> { + Err(Error::Unimplemented.into()) + } + + fn update_state( + mut deps: DepsMut, + _env: Env, + header: Self::Header, + ) -> Result, IbcClientError> { + let mut client_state: WasmClientState = read_client_state(deps.as_ref())?; + + let updated_height = Height { + revision_number: client_state.latest_height.revision_number, + revision_height: header.l1_height.revision_height, + }; + + if client_state.latest_height < header.l1_height { + client_state.data.l1_latest_height = updated_height; + update_client_state::( + deps.branch(), + client_state, + updated_height.revision_height, + ); + } + + // TODO: perhaps force the proof to be an actual inclusion proof off-chain + let account = match header.l2_ibc_contract_proof { + unionlabs::linea::proof::MerkleProof::Inclusion(inclusion) => { + // Guaranteed to success as we previously verified the proof + // which involved decoding and hashing the account. + ZkAccount::try_from(inclusion.proof.value.as_ref()).expect("impossible") + } + unionlabs::linea::proof::MerkleProof::NonInclusion(_) => { + return Err(Error::InvalidL2AccountProof.into()) + } + }; + + let consensus_state = WasmConsensusState { + data: ConsensusState { + ibc_storage_root: account.storage_root, + // must be nanos + timestamp: 1_000_000_000 * header.l2_timestamp, + }, + }; + save_consensus_state::(deps, consensus_state, &updated_height); + Ok(vec![updated_height]) + } + + fn update_state_on_misbehaviour( + deps: DepsMut, + env: Env, + _client_message: Vec, + ) -> Result<(), IbcClientError> { + let mut client_state: WasmClientState = read_client_state(deps.as_ref())?; + client_state.data.frozen_height = Height { + revision_number: client_state.latest_height.revision_number, + revision_height: env.block.height, + }; + save_client_state::(deps, client_state); + Ok(()) + } + + fn check_for_misbehaviour_on_header( + _deps: Deps, + _header: Self::Header, + ) -> Result> { + Ok(false) + } + + fn check_for_misbehaviour_on_misbehaviour( + _deps: Deps, + _misbehaviour: Self::Misbehaviour, + ) -> Result> { + Err(Error::Unimplemented.into()) + } + + fn verify_upgrade_and_update_state( + _deps: DepsMut, + _upgrade_client_state: Self::ClientState, + _upgrade_consensus_state: Self::ConsensusState, + _proof_upgrade_client: Vec, + _proof_upgrade_consensus_state: Vec, + ) -> Result<(), IbcClientError> { + Err(Error::Unimplemented.into()) + } + + fn migrate_client_store(_deps: DepsMut) -> Result<(), IbcClientError> { + Err(Error::Unimplemented.into()) + } + + fn status(deps: Deps, _env: &Env) -> Result> { + let client_state: WasmClientState = read_client_state(deps)?; + + if client_state.data.frozen_height != Height::default() { + return Ok(Status::Frozen); + } + + let Some(_) = read_consensus_state::(deps, &client_state.latest_height)? else { + return Ok(Status::Expired); + }; + + Ok(Status::Active) + } + + fn export_metadata( + _deps: Deps, + _env: &Env, + ) -> Result, IbcClientError> { + Ok(Vec::new()) + } + + fn timestamp_at_height( + deps: Deps, + height: Height, + ) -> Result> { + Ok(read_consensus_state::(deps, &height)? + .ok_or(Error::ConsensusStateNotFound(height))? + .data + .timestamp) + } +} + +fn do_verify_membership( + path: String, + storage_root: H256, + ibc_commitment_slot: U256, + storage_proof: InclusionProof, + raw_value: Vec, +) -> Result<(), Error> { + // TODO: handle error + let key = storage_proof.key.try_into().unwrap(); + + check_commitment_key(&path, ibc_commitment_slot, key)?; + + let path = path + .parse::>() + .map_err(Error::PathParse)?; + + let canonical_value = match path { + Path::ClientState(_) => { + Any::::decode(raw_value.as_ref()) + .map_err(Error::CometblsClientStateDecode)? + .0 + .encode_as::() + } + Path::ClientConsensusState(_) => Any::< + wasm::consensus_state::ConsensusState, + >::decode(raw_value.as_ref()) + .map_err(Error::CometblsConsensusStateDecode)? + .0 + .data + .encode_as::(), + _ => raw_value, + }; + + // We store the hash of the data, not the data itself to the commitments map. + let expected_value_hash = H256::from( + sha3::Keccak256::new() + .chain_update(canonical_value) + .finalize(), + ); + + // TODO: handle error + let proof_value = H256(storage_proof.proof.value.clone().try_into().unwrap()); + + if expected_value_hash != proof_value { + return Err(Error::StoredValueMismatch { + expected: expected_value_hash, + stored: proof_value, + }); + } + + let (_, _) = linea_zktrie::verify::verify_inclusion::( + &new_mimc_constants_bls12_377(), + storage_proof.leaf_index, + &storage_proof.proof, + storage_root, + // Key will be verified + Some(key), + )?; + + Ok(()) +} + +/// Verifies that no value is committed at `path` in the counterparty light client's storage. +fn do_verify_non_membership( + path: String, + storage_root: H256, + ibc_commitment_slot: U256, + noninclusion_proof: NonInclusionProof, +) -> Result<(), Error> { + // TODO: handle error + let key = noninclusion_proof.key.clone().try_into().unwrap(); + + check_commitment_key(&path, ibc_commitment_slot, key)?; + + linea_zktrie::verify::verify_noninclusion::( + &new_mimc_constants_bls12_377(), + &noninclusion_proof, + storage_root, + key, + )?; + + Ok(()) +} + +fn check_commitment_key(path: &str, ibc_commitment_slot: U256, key: H256) -> Result<(), Error> { + let expected_commitment_key = generate_commitment_key(path, ibc_commitment_slot); + + // Data MUST be stored to the commitment path that is defined in ICS23. + if expected_commitment_key != key { + Err(Error::InvalidCommitmentKey { + expected: expected_commitment_key, + found: key, + }) + } else { + Ok(()) + } +} diff --git a/light-clients/linea-light-client/src/contract.rs b/light-clients/linea-light-client/src/contract.rs new file mode 100644 index 0000000000..75f65b63d1 --- /dev/null +++ b/light-clients/linea-light-client/src/contract.rs @@ -0,0 +1,54 @@ +use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, Response}; +use ics008_wasm_client::{ + define_cosmwasm_light_client_contract, + storage_utils::{save_proto_client_state, save_proto_consensus_state}, + CustomQueryOf, InstantiateMsg, +}; +use protos::ibc::lightclients::wasm::v1::{ + ClientState as ProtoClientState, ConsensusState as ProtoConsensusState, +}; +use unionlabs::{ + encoding::{DecodeAs, Proto}, + ibc::{core::client::height::Height, lightclients::scroll::client_state::ClientState}, +}; + +use crate::{client::LineaLightClient, errors::Error}; + +#[entry_point] +pub fn instantiate( + mut deps: DepsMut>, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + let client_state = + ClientState::decode_as::(&msg.client_state).map_err(Error::ClientStateDecode)?; + + save_proto_consensus_state::( + deps.branch(), + ProtoConsensusState { + data: msg.consensus_state.into(), + }, + &Height { + revision_number: 0, + revision_height: client_state.latest_slot, + }, + ); + save_proto_client_state::( + deps, + ProtoClientState { + data: msg.client_state.into(), + checksum: msg.checksum.into(), + latest_height: Some( + Height { + revision_number: 0, + revision_height: client_state.latest_slot, + } + .into(), + ), + }, + ); + Ok(Response::default()) +} + +define_cosmwasm_light_client_contract!(LineaLightClient, Linea); diff --git a/light-clients/linea-light-client/src/errors.rs b/light-clients/linea-light-client/src/errors.rs new file mode 100644 index 0000000000..7439c481ac --- /dev/null +++ b/light-clients/linea-light-client/src/errors.rs @@ -0,0 +1,92 @@ +use ics008_wasm_client::IbcClientError; +use unionlabs::{ + encoding::{DecodeErrorOf, Proto}, + google::protobuf::any::Any, + hash::H256, + ibc::{core::client::height::Height, lightclients::wasm}, + ics24::PathParseError, +}; + +use crate::client::LineaLightClient; + +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum Error { + #[error("unable to decode storage proof")] + StorageProofDecode(#[source] DecodeErrorOf), + #[error("unable to decode counterparty's stored cometbls client state")] + CometblsClientStateDecode( + #[source] + DecodeErrorOf< + Proto, + Any, + >, + ), + #[error("unable to decode counterparty's stored cometbls consensus state")] + CometblsConsensusStateDecode( + #[source] + DecodeErrorOf< + Proto, + Any< + wasm::consensus_state::ConsensusState< + unionlabs::ibc::lightclients::cometbls::consensus_state::ConsensusState, + >, + >, + >, + ), + #[error("unable to decode client state")] + ClientStateDecode( + #[source] + DecodeErrorOf, + ), + #[error("unable to decode consensus state")] + ConsensusStateDecode( + #[source] + DecodeErrorOf< + Proto, + unionlabs::ibc::lightclients::scroll::consensus_state::ConsensusState, + >, + ), + + // REVIEW: Move this variant to IbcClientError? + #[error("consensus state not found at height {0}")] + ConsensusStateNotFound(Height), + + #[error("IBC path is empty")] + EmptyIbcPath, + + #[error("invalid commitment key, expected ({expected}) but found ({found})")] + InvalidCommitmentKey { expected: H256, found: H256 }, + + #[error("proof is empty")] + EmptyProof, + + #[error("batching proofs are not supported")] + BatchingProofsNotSupported, + + #[error("expected value ({expected}) and stored value ({stored}) don't match")] + StoredValueMismatch { expected: H256, stored: H256 }, + + #[error("unable to parse ics24 path")] + PathParse(#[from] PathParseError), + + #[error("failed to verify linea header: {0}")] + Verify(#[from] linea_verifier::Error), + + #[error("the operation has not been implemented yet")] + Unimplemented, + + #[error("error while calling custom query: {0}")] + CustomQuery(#[from] unionlabs::cosmwasm::wasm::union::custom_query::Error), + + #[error("L2 account proof must be an inclusion proof")] + InvalidL2AccountProof, + + #[error("failed to verify linea membership proof {0}")] + InvalidMembershipProof(#[from] linea_zktrie::verify::Error), +} + +impl From for IbcClientError { + fn from(value: Error) -> Self { + IbcClientError::ClientSpecific(value) + } +} diff --git a/light-clients/linea-light-client/src/eth_encoding.rs b/light-clients/linea-light-client/src/eth_encoding.rs new file mode 100644 index 0000000000..d6197099d3 --- /dev/null +++ b/light-clients/linea-light-client/src/eth_encoding.rs @@ -0,0 +1,15 @@ +use sha3::Digest; +use unionlabs::{hash::H256, uint::U256}; + +// TODO: move to unionlabs as it can be reused by any chain hosting our EVM IBC + +/// Calculates the slot for a `path` at saved in the commitment map in `slot` +/// +/// key: keccak256(keccak256(abi.encode_packed(path)) || slot) +pub fn generate_commitment_key(path: &str, slot: U256) -> H256 { + sha3::Keccak256::new() + .chain_update(sha3::Keccak256::new().chain_update(path).finalize()) + .chain_update(slot.to_be_bytes()) + .finalize() + .into() +} diff --git a/light-clients/linea-light-client/src/lib.rs b/light-clients/linea-light-client/src/lib.rs new file mode 100644 index 0000000000..b26d0adbfe --- /dev/null +++ b/light-clients/linea-light-client/src/lib.rs @@ -0,0 +1,4 @@ +pub mod client; +pub mod contract; +pub mod errors; +pub mod eth_encoding; diff --git a/uniond/proto/union/ibc/lightclients/linea/v1/linea.proto b/uniond/proto/union/ibc/lightclients/linea/v1/linea.proto new file mode 100644 index 0000000000..b2c28725fa --- /dev/null +++ b/uniond/proto/union/ibc/lightclients/linea/v1/linea.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; +package union.ibc.lightclients.linea.v1; + +option go_package = "union/ibc/lightclients/linea"; +import "ibc/core/client/v1/client.proto"; +import "union/ibc/lightclients/ethereum/v1/ethereum.proto"; + +message ClientState { + string chain_id = 1; + .ibc.core.client.v1.Height l1_latest_height = 2; + string l1_client_id = 3; + bytes l1_rollup_contract_address = 4; + bytes l1_rollup_current_l2_block_number_slot = 5; + bytes l1_rollup_current_l2_timestamp_slot = 6; + bytes l1_rollup_l2_state_root_hashes_slot = 7; + bytes l2_ibc_contract_address = 8; + bytes l2_ibc_contract_commitment_slot = 9; + .ibc.core.client.v1.Height frozen_height = 10; +} + +message ConsensusState { + bytes ibc_storage_root = 1; + uint64 timestamp = 2; +} + +message Header { + .ibc.core.client.v1.Height l1_height = 1; + .union.ibc.lightclients.ethereum.v1.AccountProof l1_rollup_contract_proof = 2; + uint64 l2_block_number = 3; + .union.ibc.lightclients.ethereum.v1.StorageProof l2_block_number_proof = 4; + bytes l2_state_root = 5; + .union.ibc.lightclients.ethereum.v1.StorageProof l2_state_root_proof = 6; + uint64 l2_timestamp = 7; + .union.ibc.lightclients.ethereum.v1.StorageProof l2_timestamp_proof = 8; + MerkleProof l2_ibc_contract_proof = 9; +} + +message MerklePath { + bytes value = 1; + repeated bytes proof_related_nodes = 2; +} + +message InclusionProof { + bytes key = 1; + uint64 leaf_index = 2; + MerklePath merkle_path = 3; +} + +message NonInclusionProof { + bytes key = 1; + uint64 left_leaf_index = 2; + MerklePath left_proof = 3; + uint64 right_leaf_index = 4; + MerklePath right_proof = 5; +} + +message MerkleProof { + oneof proof { + InclusionProof inclusion = 1; + NonInclusionProof noninclusion = 2; + } +}